【学习笔记】JavaScript-Object

189 阅读8分钟

Object 构造函数创建一个对象包装器。

JavaScript 中,几乎所有的对象都是 Object 类型的实例,都会从 Object.prototype 继承属性和方法。

语法

  1. 对象初始化器(Object initialiser)或对象字面量(literal)

    { key1: value1, key2:value2, ... }
    
  2. 以构造函数形式调用

    var obj = new Object([value])
    

    根据传入的value的类型,创建的对象有一下几种情况:

    • value 为 nullundefined 时,创建并返回一个空对象。
      var o1 = new Object()
      var o2 = new Object(undefined)
      var o3 = new Object(null)
      
    • value 为基本数据类型的值,则会构造其包装类型的对象。
      var bo = new Object(true)
      // 等价于
      var bo = new Boolean(true)
      
    • value 为引用类型的,仍然会返回这个值,并且这个值和原值一样都指向同一个引用地址。

属性

Object.length

其值为1。

Object.prototype

Object.prototype属性是一个对象类型,在该对象上添加属性可以为所有的 Object 类型的对象添加属性(无论是之前还是之后创建的对象)。

JavaScript中的所有对象都来自 Object,所有对象从 Object.prototype 继承方法和属性,Object.prototype 的更改将传播到所有对象。

Object.prototype 的属性有:

Object.prototype.constructor

用于创建一个对象的原型,返回Object构造函数的引用,而不是一个包含函数名称的字符串。

所有对象都会从它的原型上继承一个 constructor 属性

var o1 = {}
var o2 = new Object()
o1.constructor === o2.constructor === Object // true

var arr1 = []
var arr2 = new Array()
arr1.constructor === arr2.constructor === Array // true

var n1 = new Object(1)
var n2 = new Number(1)
n1.constructor === n2.constructor === Number // true

var s1 = new Object('s')
var s2 = new String('s')
s1.constructor === s2.constructor === String // true

var b1 = new Object(true)
var b2 = new Boolean(true)
b1.constructor === b2.constructor === Boolean

Object.prototype.hasOwnProperty()

判断一个对象自身是否含有指定的属性,而且该属性 不是从原型链上继承的

语法:obj.hasOwnProperty(prop)

Object.prototype.isPrototypeOf()

判断一个对象是否存在于另一个对象的原型链上。

语法:prototypeObj.isPrototypeOf(Object)

Object.prototype.propertyIsEnumerable()

判断指定的自身(非通过原型链继承)属性是否可枚举。

语法:obj.propertyIsEnumerable(prop)

Object.prototype.toLocaleString()

返回一个对象的字符串表示。

语法:obj.toLocaleString()

Object.prototype.toString()

返回一个对象的字符串表示。

语法:obj.toString()

所有对象都能转换为 [object 类型] 的格式。

Object.prototype.valueOf()

返回指定对象的原始值。

语法:obj.valueOf()

如果对象没有原始值,则返回对象本身。

JavaScript的内置对象基本都重写了该方法,对应的返回值如下表所示:

对象返回值
Boolean布尔值
Number数字值
String字符串
Array返回数组对象本身
Date存储的事件是1970年1月1日午夜开始计的毫秒数UTC
Object对象本身
Function函数本身

Math和Error对象没有valueOf方法。

Object.prototype.__proto__

指向Object的实例的原型对象。

方法

创建对象方法

Object.assign(target, ...sources)??:

通过复制一个或多个源对象的自身可枚举属性分配到目标对象上,并返回目标对象。

如果目标对象中有相同的属性,则目标对象的该属性会被源对象的属性覆盖,后面的源对象属性覆盖前面的源对象属性。

该方法使用到源对象的[[Get]]和目标对象的[[Set]],会调用相关的getter和setter。

特点

  • 只有字符串的包装对象才有自身可枚举的属性,才能被被包装成对象后再进行拷贝。
  • null和undefiend会被忽略。
  • 再拷贝时属性时引用类型的只会拷贝该引用,也称浅拷贝。
  • 异常会打断后续的拷贝任务。

一个Polyfill实现(不支持Symbol):

if (typeof Object.assign !== 'function') {
    // Object.assign必须是可写、不可枚举和可配置的
    Object.defineProperty(Object, 'assign', {
        value: function assign(target, varArgs) {
            'use strict'
            if (target === null || target === undefined) {
                throw new TypeError('不能把null和undefined转换为对象')
            }

            var to = Object(target)
            for (var index = 1; index < arguments.length; index++) {
                var nextSource = arguments[index]
                if (nextSource !== null && nextSource !== undefined) {
                    for (var nextKey in nextSource) {
                        if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
                            to[nextKey] = nextSource[nextKey]
                        }
                    {

                }
            }
            return to
        },
        writable: true,
        configurable: true
    })
}

拷贝访问器:

function completeAssign(target, ...sources) {
    sources.forEach(source => {
        let descriptors = Object.keys(source).reduce((descriptor, key) => {
            descriptors[key] = Object.getOwnPropertyDescriptor(source, key)
            return descriptors
        })
        // Object.assign 默认会拷贝可枚举的Symbols
        Object.getOwnPropertySymbols(source).forEach(sym => {
            let descriptor = Object.getOwnPropertyDescriptor(source, sym)
            if (descriptor.enumerable) {
                descriptors[sym] = descriptor
            }
        })
        Object.defineProperties(target, descriptors)
    })
    return target
}

Object.create()

使用指定的原型对象和属性创建一个新对象.

语法:Object.create(proto, [propertiesObject])

参数解析:

  • 第一个参数proto,充当新对象的原型对象;
  • 第二个参数propertiesObject的属性类型按照Object.defineProperties()的第二个参数来把 自身可枚举属性 配置到新对象上,包括 属性值和对应的属性描述符

实现类式继承:

function Shape(x, y) {
    this.x = 0
    this.y = 0
}
Shape.prototype.move = function(x, y) {
    this.x += x
    this.y += y
}

function Rectangle() {
    super.call(this)
}

Rectangle.prototype = Object.create(Shape.prototype)
Rectangle.prototype.constructor = Rectangle

var rect = new Rectangle()
rect instanceOf Rectangle // true
rect instanceOf Shape // true

继承多个对象:

function MyClass() {
    SuperClass.call(this)
    OtherSuperClass.call(this)
}

// 继承一个类
MyClass.prototype = Object.create(SuperClass.prototype)
// 混合其他
Object.assign(MyClass.prototype, OtherSuperClass.prototype)
// 重新指定constructor
MyClass.prototype.constructor = MyClass

设置/获取属性及其配置的方法

Object.defineProperty()

给对象添加/修改一个属性并指定该属性的配置,并返回该对象。

语法:Object.defineProperty(obj, propName, descriptor)

参数解析:

  • obj 是要定义属性的对象
  • propName 是要定义或修改的属性名或Symbol
  • descriptor 要定义或修改的属性描述符

通过 赋值操作 添加的是普通属性是可枚举的,在枚举对象属性时会被枚举到 for..inObject.keys,可以改变这些属性的值,也可以删除这些属性。

Object.defineProperty() 可以修改属性配置信息,但是默认情况下,Object.defineProperty() 添加的属性值是不可以修改的。

属性描述符有两种

  • 数据描述符:对象类型,是一个具有值的属性,该值可以是可写的,也可以是不可写的。
  • 访问器描述:对象类型,由 getter函数setter函数 所描述的属性。

两种描述符 都有 以下2个 可选的 属性:

  • configurable:默认false。当且仅当值为true时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上删除。
  • enumerable:默认false。当且仅当值为true时,该属性才会出现在对象的枚举属性中。

数据描述符 还有以下2个 可选的 属性:

  • writable:默认false。当且仅当值为true时,属性的 (下面的)value 才能赋值运算符改变。
  • value:属性值,默认undefined。可以是任何有效的JavaScript值。

访问器描述符 还有以下2个 可选的 属性:

  • get:属性的getter函数,默认undefined。当访问属性时会调用此函数。执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的 this 并不一定是定义该属性的对象。该函数的返回值会被用作属性的值。
  • set:属性的setter函数,默认undefined。当属性的值被修改时会调用此函数。该方法接收一个参数(也是被赋予的新值),并传入赋值时的this对象。

备注:

  • 描述符属性不一定是自身的属性,也可以能是继承来的属性。
  • 应当直接调用Object.defineProperty(),而不是调用Object的实例的defineProperty()方法。
  • 在ES6中,Object.defineProperty() 是定义key为Symbol类型的方法之一。

Object.defineProperties()

给对象添加多个属性并分别指定他们的配置。

Object.getOwnPropertyDescriptor()

返回对象指定的属性配置。

获取对象属性及属性值的方法

Object.entries()

返回给定对象自身可枚举属性的 [key,value] 数组。

Object.keys(arg)

返回一个 字符串数组,包含给定对象自身(非继承的)可枚举的属性名称

返回值数组的元素的顺序和for...in循环的循序相同,按升序输出。

在ES5中,如果arg不是一个对象(原始值),则会抛出 TypeError。在ES中,则会将非对象参数强制转换为对象在调用。for...in遍历对象的可枚举属性,包含继承的可枚举属性。

Object.keys()的Polyfill写法:

if (!Object.keys) {
    Object.keys = (function() {
        var hasOwnProperty = Object.prototype.hasOwnProperty,
            hasDontEnumbBug = !({toString: null}).propertyIsEnumerable('toString'),
            dontEnums = [
                'toString',
                'toLocaleString',
                'valueOf',
                'hasOwnProperty',
                'isPrototypeOf',
                'propertyIsEnumerable',
                'constructor'
            ],
            dontEnumsLength = dontEnums.length

        return function (obj) {
            if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) throw new TypeError('在非对象上调用Object.keys')

            var result = []
            for (var prop in obj) {
                if (hasOwnProperty.call(obj, prop)) {
                    result.push(prop)
                }
            }

            // 如果存在枚举属性bug,则把不可枚举的属性也输出
            if (hasDontEnumbBug) {
                for (var i = 0; i < dontEnumsLength; i++) {
                    if (hasOwnProperty.call(obj, dontEnums[i])) {
                        result.push(dontEnums[i])
                    }
                }
            }
            return result
        }

    })
}

Object.values()

返回一个数组,包含给定对象自身(非继承)可枚举的属性值,该数组元素的顺序和for...in循环的循序相同。

当传入一个非对象参数时,会强制转换成对象再进行调用。

Object.values('abc') // 返回 ['a', 'b', 'c']

当传入的类数组时,且类数组的key值时数值类型,当生成的数组元素顺序按数字key的升序输出。

Object.values({2: 'aa', 1: 'bb', 3: 'cc'} // 返回 ['bb', 'aa', 'cc']

Object.values()的Polyfill写法:

if (!Object.values) {
    Object.values = function(obj) {
        if (obj !== Object(obj)) {
            throw new TypeError('请传入一个对象参数')
        }
        var values = [], key
        for (key in obj) {
            if(Object.prototype.hasOwnProperty.call(obj, key)) {
                values.push(obj[key])
            }
        }
        return values
    }
}

Object.getOwnPropertyNames()

返回一个数组,它包含了指定对象所有 可枚举或不可枚举 的属性名。

Object.getOwnPropertySymbols()

返回一个数组,包含了指定对象自身所有的 符号(Symbol) 属性。

对象不可用的方法

Object.freeze()

冻结对象,其他代码不能删除或更改任何属性。

Object.isFrozen()

判断对象是否以冻结。

Object.isExtensible()

判断对象是否可扩展。

Object.isSealed()

判断对象是否已经密封。

Object.preventExtensions()

防止对象的任何扩展。

Object.seal()

防止其他代码删除对象的属性。

原型对象的方法

Object.getPrototypeOf()

返回指定对象的原型对象。

Object.setPrototypeOf()

设置对象的原型(即设置对象的prototype属性)。

比较的方法

Object.is()

比较两个值是否相同,所有的NaN值都相等。