Object
下的方法大致如下:
Object.assign
Object.is
Object.create
Object.setPrototypeOf
Object.getPrototypeOf
Object.defineProperty
Object.defineProperties
Object.getOwnPropertyDescriptor
Object.getOwnPropertyDescriptors
Object.getOwnPropertyNames
Object.keys
Object.getOwnPropertySymbols
Object.entries
Object.fromEntries
Object.freeze
Object.isFrozen
Object.seal
Object.isSealed
Object.preventExtensions
Object.isExtensible
Object.hasOwn
1.Object.assign
Object.assign
是一个 JavaScript
方法,用于将一个或多个源对象的属性拷贝到目标对象中。
它返回目标对象,并且支持浅拷贝,适合用于对象的合并、克隆等操作。
Object.assign(target, ...sources)
它具有以下特点:
- 浅拷贝:
Object.assign
只做浅拷贝,嵌套对象会引用相同的对象。 - 覆盖顺序:如果多个源对象中存在同名属性,最后一个对象的属性值会覆盖前面的。
- 无法拷贝不可枚举属性:
Object.assign
只会拷贝源对象中的可枚举属性。 - 原型链:它不会拷贝源对象的原型链属性,仅拷贝对象本身的属性。
1-1.合并对象
const target = { a: 1, b: 2 }
const source = { b: 4, c: 5 }
const result = Object.assign(target, source)
console.log(result) // 输出 { a: 1, b: 4, c: 5 }
console.log(target) // 输出 { a: 1, b: 4, c: 5 } (目标对象被修改)
1-2.对象浅拷贝
const original = { name: 'John', age: 30 }
const copy = Object.assign({}, original)
console.log(copy) // 输出 { name: 'John', age: 30 }
由于是浅拷贝,因此嵌套对象会引用相同的对象:
const obj1 = { a: { b: 1 } }
const obj2 = Object.assign({}, obj1)
obj2.a.b = 2
console.log(obj1.a.b) // 输出 2
2.Object.is
Object.is(v1, v2)
Object.is
用于比较两个值。它是为了弥补全等运算符在某些情况下的判断缺陷。譬如:
console.log(+0 === -0) // true
console.log(NaN === NaN) // false
console.log(Object.is(+0, -0)) // false
console.log(Object.is(NaN, NaN)) // true
对于 Object.is
方法来说,它与全等运算符的区别就在于对于 +0
与 -0
、NaN
与 NaN
的判断。
所以大多数情况下使用下全等运算符即可,上面的两种情况使用 Object.is
。
3.Object.create
Object.create(proto, propertiesObject)
Object.create
会创建一个空对象,并将该对象的 __proto__
属性指向第一个参数。 第二个参数(可省略)的格式参照了 Object.defineProperties
,在实际执行时,会将该参数混入到创建的空对象中。
// polyfill
if (typeof Object.create !== "function") {
Object.create = function (proto, propertiesObject) {
if (typeof proto !== 'object' && typeof proto !== 'function') {
throw new TypeError('Object prototype may only be an Object: ' + proto);
} else if (proto === null) {
throw new Error("This browser's implementation of Object.create is a shim and doesn't support 'null' as the first argument.");
}
if (typeof propertiesObject !== 'undefined') throw new Error("This browser's implementation of Object.create is a shim and doesn't support a second argument.");
function F() {}
F.prototype = proto;
return new F();
};
}
4.Object.setPrototypeOf
Object.setPrototypeOf(obj, proto)
设置一个指定的对象的原型 (即, 内部 [[Prototype]]
属性)到另一个对象或 null
。
TIP
警告: 由于现代 JavaScript
引擎优化属性访问所带来的特性的关系,更改对象的 [[Prototype]]
在各个浏览器和 JavaScript
引擎上都是一个很慢的操作。其在更改继承的性能上的影响是微妙而又广泛的,这不仅仅限于 obj.__proto__ = ...
语句上的时间花费,而且可能会延伸到任何代码,那些可以访问任何 [[Prototype]]
已被更改的对象的代码。如果你关心性能,你应该避免设置一个对象的 [[Prototype]]
。相反,你应该使用 Object.create()
来创建带有你想要的 [[Prototype]]
的新对象。
Object.setPrototypeOf()
是 ECMAScript 6
最新草案中的方法,相对于 Object.prototype.__proto__
,它被认为是修改对象原型更合适的方法。
// polyfill
if (!Object.setPrototypeOf) {
// 仅适用于Chrome和FireFox,在IE中不工作:
Object.setPrototypeOf = function(obj, proto) {
if(obj.__proto__) {
obj.__proto__ = proto;
return obj;
} else {
// 如果你想返回 prototype of Object.create(null):
var Fn = function() {
for (var key in obj) {
Object.defineProperty(this, key, {
value: obj[key],
});
}
};
Fn.prototype = proto;
return new Fn();
}
}
}
PS: Object.create
与 Object.setPrototypeOf
的区别:
// Object.create(target, property) 与 Object.setPrototypeOf(v1, v2)
var o = {
id: 19
}
var a = Object.create(o)
var b = {}
Object.setPrototypeOf(b, o)
console.log('a', a)
console.log('b', b)
两者实现效果一致 都能将某一实例的原型 __proto__
指向新的指定地址。
但在方法使用上,有一些区别:
Object.create(o)
有返回值 该返回值的__proto__
指向o
。Object.setPrototypeOf(v1, v2)
也有返回值,而且返回值等同于v1
,v1
的__proto__
指向v2
。Object.create
可以传入第二个参数。 该参数的形式与Object.defineProperties
的第二个参数形式一致。执行结果会将该参数合并进所创建的新对象中。譬如:
var o = Object.create(null, {
prop: {
value: 'propValue',
enumerable: true,
write: true,
configable: true
}
})
console.log(o) // { prop: 'propValue' }
TIP
__proto__
已逐渐被废弃, [[prototype]]
也是不支持实例直接访问的。所以更好的方式是利用 constructor
的 prototype
属性。
5.Object.getPrototypeOf
Object.getPrototypeOf(obj)
与其说是 Object.getPrototypeOf
不如说是 Object.get[[Prototype]]Of
。
该 API
获取的是实例的 [[prototype]]
,而不是构造函数的 prototype
。
6.Object.defineProperty
Object.defineProperty(obj, property, descriptor)
其中,描述符 descriptor
分为 数据描述符与访问器描述符。
描述符descriptor是一个对象。数据描述符与访问器描述符共有的键属性是:
enumerable
属性是否可被枚举。是否可以在for...in
循环和Object.keys()
中被枚举。configable
属性是否可以更改描述符(除value
和writable
外的描述符),是否可被删除。
另外,数据描述符所特有的属性是:
value
属性的值。writable
属性是否可被更改,可被重写。
访问器描述符所特有的属性是:
get
属性的getter
函数。当属性被访问时,会调用此函数。set
属性的setter
函数。当属性被设置时,会调用此函数。
数据描述符与访问器描述符不能混用。也就是要么使用数据描述符,要么使用访问器描述符。只能使用其一。
7.Object.defineProperties
Object.defineProperties(obj, properObj)
与 Object.defineProperty
类似,只不过这个是复数属性值。
var obj = {}
Object.defineProperties(obj, {
id: {
configable: true,
enumerable: true,
value: 19,
writable: true
},
name: {
configable: false,
enumerable: true,
get() {
return ''
},
set(value) {
this.id = value
}
}
})
8.Object.getOwnPropertyDescriptor
Object.getOwnPropertyDescriptor(obj, property)
var obj = { id: 19 }
var descriptor = Object.getOwnPropertyDescriptor(obj, 'id')
console.log(descriptor)
9.Object.getOwnPropertyDescriptors
Object.getOwnPropertyDescriptors(obj)
var obj = { id: 19, name: 'yxp' }
var descriptors = Object.getOwnPropertyDescriptors(obj)
console.log(descriptors)
// {
// id: {value: 19, writable: true, enumerable: true, configurable: true},
// name: {value: 'yxp', writable: true, enumerable: true, configurable: true}
// }
10.Object.getOwnPropertyNames
Object.getOwnPropertyNames(obj)
var obj = { id: 19 }
Object.defineProperty(obj, 'name', {
configable: true,
enumerable: false,
value: 'yxp',
writable: true
})
var propertyNames = Object.getOwnPropertyNames(obj)
console.log(propertyNames)
// ['id', 'name']
11.Object.keys
Object.keys(obj)
与 Object.getOwnPropertyNames
都是返回自身的属性,不会返回原型链上的。
如果某属性的属性描述符 enumerable
为 false
(不可枚举),则 Object.keys
不会返回该属性。
而 Object.getOwnPropertyNames
会返回该属性。
var obj = { id: 19 }
Object.defineProperty(obj, 'name', {
configable: true,
enumerable: false,
value: 'yxp',
writable: true
})
var propertyNames = Object.keys(obj)
console.log(propertyNames)
// ['id']
12.Object.getOwnPropertySymbols
该方法针对的是对象属性中利用 Symbol()
创建的属性。
因为 Object.keys
和 Object.getOwnPropertyNames
都不能检索出对象属性中的 Symbol
。
所以 Es6
额外提供了该方法。
var myName = Symbol('my name')
var job = Symbol('my job')
var obj = {
[myName]: 'yxp',
[job]: 'programmer'
}
// 1.Object.keys
var keys = Object.keys(obj)
console.log(keys)
// 2.Object.getOwnPropertyNames
var propertyNames = Object.getOwnPropertyNames(obj)
console.log(propertyNames)
// 3.Object.getOwnPropertySymbols
var symbols = Object.getOwnPropertySymbols(obj)
console.log(symbols)
13.Object.entries
Object.entries(obj)
返回一个子项是该对象属性键值对的数组的二维数组。
与 Object.keys
相同的一点是,不会涉及到原型上的属性或者是不可枚举的属性。
function F() {
this.id = 19
this.name = 'yxp'
}
F.prototype.age = 'my-age'
var obj = new F()
Object.defineProperty(obj, 'job', {
configable: true,
enumerable: false,
value: 'my-job',
writable: true
})
var entries = Object.entries(obj)
console.log(entries)
// [['id', 19], ['name', 'yxp']]
14.Object.fromEntries
Object.fromEntries(iterable)
把键值对列表(二维数组)转换为一个对象。与 Object.keys
是相反操作。
参数 iterable
:类似 Array
、 Map
或者其它实现了可迭代协议的可迭代对象。
返回值:一个由该迭代对象条目提供对应属性的新对象。
var arr = [['id', 19], ['name', 'yxp']]
var fromEntries = Object.fromEntries(arr)
console.log(fromEntries)
15.Object.freeze
Object.freeze(obj)
冻结一个对象,该对象不能修改现有属性,不能再添加新属性,不能更改原型指向。
另外该对象的所有属性的属性操作符也不能再操作更改( configable
会自动变为 false
)。
var a = {id: 19}
var desA = Object.getOwnPropertyDescriptors(a)
console.log(desA) // {id: {value: 19, writable: true, enumerable: true, configurable: true}}
// b 和 a 指向同一块内存地址
var b = Object.freeze(a)
var desB = Object.getOwnPropertyDescriptors(b)
console.log(desB) // {id: {value: 19, writable: false, enumerable: true, configurable: false}}
Object.freeze
实际上是浅冻结。
'use strict'
var obj = {
id: 10,
info: {
id: 19
}
}
Object.freeze(obj)
obj.info.id = '19' // work
obj.id = '10' // error
使用递归函数实现一个深冻结:
function deepFreeze(obj) {
// 使用Object.getOwnPropertyNames,而不是Object.keys。因为要对不可枚举的属性也做冻结处理。
var propertyNames = Object.getOwnPropertyNames(obj)
propertyNames.forEach(item => {
var prop = obj[item]
if (typeof prop === 'object' && prop !== 'null') {
deepFreeze(prop)
}
})
return Object.freeze(obj)
}
TIP
Object.freeze
在 vue
项目当中可以用来做性能优化。
16.Object.isFrozen
Object.isFrozen(obj)
判断一个对象是否被冻结。
var obj = {
id: 19
}
Object.freeze(obj)
console.log(Object.isFrozen())
17.Object.seal
Object.seal(obj)
使一个对象密封。该对象不能再添加新属性,不能更改原型指向。
另外该对象的所有属性的属性操作符也不能再操作更改( configable
会自动变为 false
)。
var obj = {
id: 10,
info: {
id: 19
}
}
var descriptor1 = Object.getOwnPropertyDescriptors(obj)
console.log(descriptor1)
/*
{
id: {value: 10, writable: true, enumerable: true, configurable: true},
info: {value: {…}, writable: true, enumerable: true, configurable: true}
}
*/
var o = Object.seal(obj)
console.log(o === obj)
var descriptor2 = Object.getOwnPropertyDescriptors(obj)
console.log(descriptor2)
/*
{
id: {value: 10, writable: true, enumerable: true, configurable: false},
info: {value: {…}, writable: true, enumerable: true, configurable: false}
}
*/
TIP
可以发现,Object.seal(obj)
会将 configable
属性描述符修改为 false
,但并不会更改现有属性的 writable
属性描述符。这也是它与 Object.freeze
的唯一不同之处。
Object.seal(obj)
可以对现有属性进行修改操作。
18.Object.isSealed
Object.isSealed(obj)
判断一个对象是否被密封。
19.Object.preventExtensions
Object.preventExtensions(obj)
让一个对象变的不可扩展,也就是永远不能再添加新的属性,也不能更改原型指向。(但现有属性的属性描述符并不受影响)。
可以发现,对于对象的限制程度,这三个方法是依次递减的:
Object.freeze() > Object.seal() > Object.preventExtensions(obj)
20.Object.isExtensible
Object.isExtensible(obj)
判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。
默认情况下,对象是可扩展的:即可以为他们添加新的属性。以及它们的 __proto__
属性可以被更改。
Object.preventExtensions
,Object.seal
或 Object.freeze
方法都可以标记一个对象为不可扩展(non-extensible
)。
21.Object.hasOwn
Object.hasOwn(obj, prop)
判断一个对象是否拥有某属性。
function F() {
this.id = 19
}
F.prototype.name = 'yxp'
var obj = new F()
// 添加一个不可枚举属性
Object.defineProperty(obj, 'job', {
configable: true,
enumerable: true,
value: 'programmer',
writable: true
})
console.log(obj) // {id: 19, job: 'programmer'}
console.log(Object.hasOwn(obj, 'id')) // true
console.log(Object.hasOwn(obj, 'name')) // false
console.log(Object.hasOwn(obj, 'job')) // true
console.log(obj.hasOwnProperty('id')) // true
console.log(obj.hasOwnProperty('name')) // false
console.log(obj.hasOwnProperty('job')) // true
可以发现,无论是 Object.hasOwn()
还是 Object.prototype.hasOwnProperty()
,都不会拿到原型上的属性,但可以拿到不可枚举的属性。(即writable
属性描述符是 false
)。