阅读 66

【学习笔记】JavaScript-Object

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值都相等。

文章分类
前端
文章标签