对象的扩展
属性名表达式
JavaScript 定义属性有两种方法:
1 | obj.foo = true |
ES6 允许字面量定义对象时使用第二种方法:
1 | let propKey = "foo" |
方法的 name 属性
方法的 name 属性也返回函数名。
1 | const person = { |
如果对象的方法使用了 getter 和 setter,则 name 的属性不是在该方法上面,而是在该方法属性的描述对象的 get 和 set 属性上面,返回值是方法名前面加上 get 和 set
1 | const obj = { |
如果对象的方法是一个 Symbol 值,那么 name 属性返回的是这个 Symbol 值的描述。
1 | const key1 = Symbol("description") |
Object.is()
ES5 比较两个值是否相等,只有两个相等运算符:==和===,他们都有个缺点,前者会自动转换数据类型,后者 NaN 不等于自身,以及+0 等于-0。
ES6 提出了Same-value-equality算法来解决这个问题,Object.is()就是部署这个算法的新方法。
不同之处只有两个:
1 | ;+0 === -0 //true |
ES5 可以用代码部署:
1 | Object.defineProperty(Object, 'is' { |
Object.assign()
Object.assign 方法用于将源对象的所有 可枚举 属性复制到目标对象。第一个参数是目标对象,后面的参数都是源对象
1 | var target = { a: 1 } |
如果只有一个参数,Object.assign()会直接返回该参数。
如果参数不是对象,会先转成对象,然后返回,由于 undefined 和 null 无法转变,所以如果将他们作为参数,就会报错。但是,如果 undefined 和 null 不在首参数,那就不会报错。
其他类型的值(数值、字符串和布尔值)不在首参数也不会报错,但是,除了字符串会以数组形式复制到目标对象,其他值都不会产生效果。
Object.assign()方法实行的赋值是浅复制,如果源对象的某个属性的值是对象,那么目标对象得到的是这个对象的引用
1 | var obj1 = { a: { b: 1 } } |
对于这种嵌套对象,一旦遇到同名属性,处理的方法是替换而不是添加。
1 | var target = { a: { b: "c", d: "e" } } |
Object.assign()可以用来处理数组,但是会把数组当成对象来处理。
1 | Object.assign([1, 2, 3], [4, 5]) //[4, 5, 3] |
常见用途:
- 为对象添加属性:
1 | class Point { |
- 为对象添加方法:
1 | Object.assign(SomeClass.prototype, { |
- 克隆对象
1 | //只克隆对象自身值 |
- 合并多个对象
1 | const merge = (target, ...sources) => Object.assign(target, ...sources) |
- 为属性指定默认值
1 | const DEFAULTS = { |
属性的可枚举性
对象的每一个属性都有一描述对象,用于控制该属性的行为,Object.getOwnPropertyDescriptor 方法可以获取该属性的描述对象。
1 | let obj = { foo: 123 } |
enumerable 属性成为可枚举性,如果该属性为 false,就表示某些操作会忽略当前属性。
ES5 有三个操作会忽略 enumerable 为 false 的属性:
- for…in 循环:只遍历对象自身和继承的可枚举属性。
- Object.keys(): 返回对象自身的所有可枚举属性的键名。
- JSON.stringify():只串行化对象自身的可枚举属性。
ES6 规定,所有 Class 的原型的方法都是不可枚举的
属性的遍历
ES6 共有五种方法遍历对象的属性:
- for…in 循环:遍历对象自身和继承的可枚举属性(不含 Symbol 属性)。
- Object.keys():返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)。
- Object.getOwnPropertyNames(obj):返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)。
- Object.getOwnPropertySymbols(obj): 返回一个数组,包含对象自身的所有 Symbol 属性。
- Reflect.ownKeys(obj):返回一个数组,包含对象自身的所有属性,不管属性名是 Symbol 还是字符串,也不管是否可枚举。
以上方法遍历对象属性时都遵守同样的属性遍历次序规则: - 首先遍历所有属性名为数值的属性,按照数字排序。
- 其次遍历所有属性名为字符串的属性,按照生成时间排序。
- 最后遍历所有属性名为 Symbol 值的属性,按照生成时间排序。
proto属性
proto属性用来读取或设置当前对象的 prototype 对象。
1 | //ES6写法 |
在实现上,proto调用的是 Object.prototype.proto,实现如下:
1 | Object.defineProperty(Object.prototype, "__proto__", { |
如果一个对象本身部署了proto属性,则该属性的值就是对象的原型。
1 | Object.getPrototypeOf({ __proto__: null }) //null |
Object.setPrototypeOf()
Object.setPrototypeOf()用于设置一个对象的 prototype 对象,返回参数本身。
1 | let proto = {} |
如果第一个参数不是对象,就会自动转为对象,但是由于返回的还是第一个参数,所以这个操作不会有任何的结果。
如果第一个参数是 undefined 或 null,则会报错
Object.setPrototypeOf()
Object.setPrototypeOf()用于读取一个对象的 prototype 对象。
如果第一个参数不是对象,则会自动转为对象。
如果第一个参数是 undefined 或 null,则会报错
Object.keys()、Object.values()、Object.entries()
Object.keys()返回一个数组,成员是参数对象自身的(不含继承)所有可遍历属性的键名。
Object.values()返回一个数组,成员是参数对象自身的(不含继承)所有可遍历属性的键值。
Object.entries()返回一个数组,成员是参数对象自身的(不含继承)所有可遍历属性的键值对数组。
实现 Object.entries():
1 | //Generator函数版本 |
对象的扩展运算符
1 | let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 } |
扩展运算符可以用于合并两个对象
1 | let ab = { ...a, ...b } |
Object.getOwnPropertyDescriptors()
ES5 的 Object.getOwnPropertyDescriptor()方法用来返回某个对象属性的 descriptor。
1 | var obj = { p: "a" } |
ES2017 引入了 Object.getOwnPropertyDescriptors()方法,返回指定对象所有自身属性(非继承)的 descriptor。
1 | const obj = { |
使用 Object.prototype.toString()进行类型判断
Object.prototype
能够更好的识别出类型,typeof
只能识别出基础类型。
1 | let date = new Date() |
可以识别的类型有:
- 123: [object Number]
- ‘1234’: [object String]
- true: [object Boolean]
- undefined: [object Undefined]
- null: [object null]
- {foo: ‘bar’}: [object Object]
- [1, 2, 3]: [object Array]
- new Date(): [object Date]
- new Error(): [object Error]
- /a+/g: [object RegExp]
- function a() {}: [object Function]
- Math: [object Math]
- JSON: [object JSON]
- arguments: [object arguments]