最近在复习JS
的基础知识,到了对象的部分,写一篇文章来总结一下
修改对象扩展性的三个方法
JS
中有三个修改对象扩展性的方法:Object.preventExtensions, Object.seal, Object.freeze
,并且有与之对应的三个判断方法:Object.isExtensible, Object.isSealed, Object.isFrozen
,下面总结一下
Object.preventExtensions
这个方法会让对象不可扩展,也就是不能添加新的属性,并且一旦对象变为不可扩展之后,就不能再让其恢复扩展性,改方法只会对自身属性有效,不会对原型上的属性起作用,并且该方法会让__proto__
不可变,这一点需要注意。下面的代码会报错
let obj = {}
Object.preventExtensions(obj)
// 报错
obj.__proto__ = {
a: 1
}
Object.seal
这个方法会封闭原对象,就是在Object.preventExtensions
的基础上,将每个属性(包括不可枚举属性)的configurable
设置为false
,这样就不能删除原有属性,也不能将原有的属性设置为getter, setter
。
Object.freeze
这个方法会在Object.seal
的基础上,将每个属性(包括不可枚举的属性)的writable
设置为false
,这样每个属性的值就不能改变,即便通过setter
也不能改变。
一个小总结
除了前面提到的内容外,这三个方法的返回值都是源对象,也就是在源对象的基础上进行操作。此外,这三个方法对对象的操作只有一层(类似浅拷贝)
let obj = {
a: 1,
o: {
b: 2
}
}
Object.freeze(obj)
obj.o.c = 3 // 成功
一个有趣的地方:
let obj = {} Object.preventExtensions(obj) Object.isSealed(obj) // true Object.isFrozen(obj) // true
对于一个空对象,禁止其扩展后,由于该对象没有任何属性,因此
isSealed
和isFrozen
也就变成了true
判断对象是否存在某个属性
in操作符
用in
操作符判断属性是否存在时,会在目标对象的原型上进行查找,该操作符支持不可枚举属性,支持Symbol
Object.hasOwnProperty
该方法只会查找对象自身是否有目标属性,支持不可枚举属性,支持Symbol
遍历对象的方法
对于遍历对象属性的方式,我分成了两类,ES5及之前提供的,以及ES6提供的,区分依据是是否支持遍历Symbol
类型
ES5方法
for ... in ...
该方法可遍历对象自身与原型上的可枚举属性,不支持Symbol
Object.getOwnPropertyNames
该方法可遍历对象自身的所有属性,无论是否可枚举,不支持Symbol
Object.keys
这个方法平时用的应该比较多,它能够遍历对象自身的所有可枚举属性,不支持Symbol
ES6方法
Object.getOwnPropertySymbols
该方法是用来遍历Symbol
类型的属性的,只能遍历对象自身,无论属性是否可枚举
Object.getOwnPropertyDescriptors
我认为严格来说这个方法不能算是一个遍历对象的方法,但是它确实能够遍历对象,姑且把它也算上吧,害。
这个方法能够遍历对象自身所有属性,无论是否可枚举,支持Symbol
类型的属性
Object.assign
这个方法需要注意的点比较多:
- 这个方法的
length
属性是2,尽管它能接受多个源对象 - 第一个参数是目标对象,不能是
null
或者undefined
- 这个方法只会拷贝可枚举的属性到目标对象上
- 当源对象上有
getter
时,这个方法会失真,只会拷贝值
一个基础版的Object.assign
方法的实现
// 为了简便,没有处理这个方法的length属性
Object.assign = function(target, ...sources) {
if (target == null) {
throw new TypeError('Cannot convert undefined or null to object')
}
target = Object(target)
for (let i = 0, len = sources.length; i < len; i++) {
let source = sources[i]
// 跳过null和undefined
if (source != null) {
// 使用Object.keys找出可枚举属性
Object.keys(source).forEach(key => target[key] = source[key])
}
}
return target
}
下面是一个支持getter, setter
版本的实现
Object.assign = function(target, ...sources) {
if (target == null) {
throw new TypeError('Cannot convert undefined or null to object')
}
target = Object(target)
for (let i = 0, len = sources.length; i < len; i++) {
let source = sources[i]
if (source != null) {
const descriptors = Object.keys(source).reduce((obj, key) => {
obj[key] = Object.getOwnPropertyDescriptor(obj, key)
return obj
}, {})
}
Object.definePropertyDescriptors(target, descriptors)
}
return target
}
另附一个网上看到的题:
const v1 = 123
const v2 = '123' // 自动装箱/包装类
const v3 = true
const v4 = function() {} // 函数的所有属性都不可枚举
const v5 = Object.assign({}, v1, v2, v3, v4)
console.log(v5)
// {
// 0: 1,
// 1: 2,
// 2: 3
// }