[JS]对象

312 阅读5分钟

本文为重读《JavaScript权威指南》CH6对象的笔记,理解不到位的地方欢迎指出与交流,谢谢!

创建对象

对象直接量

对象直接量是一个表达式,这个表达式的每次运算都会创建并初始化一个对象。每次计算对象直接量的时候,也都会计算它的每个属性的值。

如果在一个重复调用的函数中的循环体内使用了对象直接量,他将创建很多新对象,而且每次创建的对象的属性值也可能不同。

通过new创建对象

例如 new Date()、new Array()、new Object()、new RegExp('js')等

原型

所有通过对象直接量创建的对象都具有同一个原型对象,并可以通过Object.prototype获得对原型对象的引用。
通过new关键字和构造函数调用创建的对象的原型就是构造函数的prototype属性的值。

Object.prototype是所有的内置构造函数以及大部分自定义的构造函数的原型。所以例如new Date()创建的Date对象的属性同时继承自Date.prototype和Object.prototype。

let obj = { a:1, b:2 };
obj.constructor === Object.prototype.constructor // true
let newObj = new Object({ c: 1, d: 2 });
newObj.constructor === Object.prototype.constructor // true
let date = new Date();
date.constructor === Date.prototype.constructor // true
date.constructor === Object.prototype.constructor // false

Object.create()

传入的第一个参数为必填项,是创建对象的原型,第二个参数为可选项,用以对对象的属性进行进一步描述。

let o1 = Object.create({ a:1, b:2 });
let o2 = Object.create(null); // 创建一个没有原型的新对象
let o3 = Object.create(Object.prototype); // 普通的空对象,与{}或new Object()创建的对象一样

属性的查询和设置

属性设置失败的场景

  • 属性是只读的(defineProperty()有一个例外)
  • 属性是继承的且也是只读的,不能通过同名自由属性覆盖只读的继承属性
  • 没有setter方法可供调用且该对象是不可扩展的

属性访问错误

  • 未找到属性值不会报错,返回undefined
  • 试图查询不存在的对象的属性值会报错

删除属性

  • delete只能删除自有属性,不能删除继承属性
  • delete运算符只是断开属性和宿主之间的联系,而不会去操作属性:已经删除的属性的其他引用可能依然存在,这样可能会因此而造成内存泄露。所以在销毁对象的时候,要遍历属性中的属性,依次删除。
  • delete不能删除那些可配置性为false的属性

检测属性

检测某个属性是否存在于某个对象的方法:

  • in:可以用于检测自有属性或者继承属性
  • hasOwnProperty():检测是否是自有属性,继承属性会返回false
  • propertyIsEnumerable():只有自有属性且可枚举性为true时才返回true

枚举属性

  • for in可以遍历所有可枚举的属性(包括自有和继承)
  • Object.keys():返回一个数组,该数组由所有可枚举的自有属性的名称组成
  • Object.getOwnPropertyNames():返回对象的所有自有属性的名称,不仅仅是可枚举的属性。

属性getter和setter

存取器属性:属性可以是一个/两个方法,这两个方法就是getter和setter。

  • 该属性不具有可写性(即不可修改属性的值)
  • 同时具有setter/getter方法,那么它是一个读/写属性,不然就是只读属性或者只写属性。
  • 读取只写属性总是返回undefined
let obj = {
  num: 0,
  get next() { return this.num++ },
  set next(n) { 
    if(n >= this.num) this.num = n;
    else throw 'ERROR: 序列号的值不能比当前值小';
  }  
}
obj; // { num: 0 }
obj.next; // 0;
obj; // { num: 1 }
obj.next = 5; // 调用setter方法,把=右边的值当做参数传入
obj; // { num: 5 } 

属性的特性

数据属性的四个特性:

  • 值 value
  • 可写性 writable
  • 可枚举性 enumerable
  • 可配置性 configurable

存取器属性的四个特性:

  • 读取 get: 函数
  • 写入 set: 函数
  • 可枚举性 enumerable
  • 可配置性 configurable

es5定义了一个名为“属性描述符”的对象,这个对象代表上述四种特性。

Object.getOwnPropertyDescriptor()只能得到自有属性的描述符。要想获得继承属性的特性,需要遍历原型链,通过Object.getPrototypeOf()获得对象的原型(对象),并通过原型来查询属性的描述符。

Object.getOwnPropertyDescriptor({ x:1 }, 'x'); // {value: 1, writable: true, enumerable: true, configurable: true}
Object.getOwnPropertyDescriptor(obj, 'next'); // {enumerable: true, configurable: true, get: ƒ, set: ƒ}

对象的三个属性

原型属性

  • 将对象传入Object.getPrototypeOf()可以查询它的原型
  • 想要检测一个对象是否是另一个对象的原型(或处于原型链中),可以通过p.isPrototypeOf(o)的方法来检测p是否是o的原型
  • Mozilla对外暴露了一个属性__proto__用以直接查询/设置对象的原型,但并不推荐使用

类属性

类属性:是一个字符串,用以表示对象的类型信息。

  • 要想获得对象的类,可以调用对象的toString()方法
  • 但很多对象集成的toString()方法被重写了,所以可以使用Object.prototype.toString.call(obj)的方法
let obj = { x:1 };
obj.toString(); // "[object Object]"
obj.toString = function() { console.log('123') };
obj.toString(); // "123"
Object.prototype.toString.call(obj) // "[object Object]"

可扩展性

可扩展性:用以表示是否可以给对象添加新属性。

  • 正常情况下,所有内置对象和自定义对象都是可扩展的,除非将其转换成了不可扩展
  • 通过将对象传入Object.isExtensible()方法来判断该对象是否是可扩展的
  • 可将对象传入Object.preventExtensible()来将对象转换为不可扩展的,但这只影响到了对象本身,对于其原型无影响,如果原型增加了属性,对象依然会继承新增的属性
  • Object.seal()与Object.isExtensible()效果类似,还可将对象的所有自有属性都设置为不可配置
  • Object.freeze()与Object.seal()类似,还可将自有的所有数据属性设置为只读

序列化对象

  • 对象一次完整的序列化,就是把对象进行了一次深拷贝操作
  • 支持对象、数组、字符串、无穷大数字、布尔值和null,并且它们可以序列化和还原
  • NaN、Infinity和-Infinity序列化的结果是null
  • 日期对象序列化的结果是ISO格式的日期字符串
  • 函数、RegExp、Error对象和undefined值不能序列化和还原
  • 只能序列化可枚举的自有属性,不能序列化的属性会被省略掉