浅析ES6对象相关方法的缘起及使用:Object.is()、Object.keys(),Object.values()、Object.getOwnPropertyDescriptors()和__proto__属性,Object.setPrototypeOf(),Object.getPrototypeOf()
一、Object.is()
1、缘起
ES5比较两个运算符是否相等,有两种运算符:相等运算符和严格相等运算符。
缺点:前者会自动转数据类型,后者的NaN不等于自身,以及+0等于-0。
所以缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。
ES6 提出“Same-value equality”(同值相等)算法,用来解决这个问题。
2、示例
Object.is 就是部署这个算法的新方法,它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。不同之处只有两个:一是+0不等于-0,二是NaN等于自身。
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
// 使用ES5实现Object.is()
Object.defineProperty(Object, 'is', {
value: function(x, y) {
if (x === y) {
// 针对+0 不等于 -0的情况
return x !== 0 || 1 / x === 1 / y;
}
// 针对NaN的情况
return x !== x && y !== y;
},
configurable: true,
enumerable: false,
writable: true
});
二、Object.keys(),Object.values()
1、Object.keys()
ES5 引入了Object.keys
方法,返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名
2、Object.values()
Object.values
方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。返回数组的成员顺序与对象里的属性顺序一致。
const obj = { 100: 'a', 2: 'b', 7: 'c' , 'g': 'f'};
Object.values(obj)
// (4) ['b', 'c', 'a', 'f']
注意点:
(1)只返回对象自身的可遍历属性。
Object.create方法的第二个参数添加的对象属性,如果不显式声明,默认是不可遍历的,因为其属性描述对象的enumerable默认是false,Object.values不会返回这个属性。只要把enumerable改成true,Object.values就会返回该属性的值。
(2)Object.values
会过滤属性名为 Symbol 值的属性
Object.values({ [Symbol()]: 123, foo: 'abc' }); // ['abc']
const obj = Object.create({}, { test: {
value: 30,
enumerable: true
}
});
Object.values(obj) // [30]
(3)如果参数不是对象,Object.values
会先将其转为对象。
这里有 2 种情况需要注意下:
- 由于数值和布尔值的包装对象,都不会为实例添加非继承的属性。所以,Object.values
会返回空数组。
- 如果Object.values
方法的参数是一个字符串,会返回各个字符组成的一个数组
Object.values(20) // []
Object.values(true) // []
Object.values('42') // (2) ['4', '2']
当为 string 时,也是先转为包装类型的对象,就等价于下面这样
三、Object.getOwnPropertyDescriptors()
1、使用介绍
ES5 的 Object.getOwnPropertyDescriptor(obj, prop) 方法会返回某个对象属性的描述对象(descriptor),即返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)。如果指定的属性存在于对象上,则返回其属性描述符对象(property descriptor),否则返回 undefined。
在 ES5 中,如果该方法的第一个参数不是对象(而是原始类型),那么就会产生出现 TypeError。而在 ES2015 第一个的参数不是对象的话就会被强制转换为对象。
ES2017 引入了Object.getOwnPropertyDescriptors() 方法,返回指定对象所有自身属性(不包括非继承属性)的描述对象。
const obj = {
foo: 123,
get bar() { return 'abc' }
};
d = Object.getOwnPropertyDescriptor(obj, 'foo');
// {value: 123, writable: true, enumerable: true, configurable: true}
d = Object.getOwnPropertyDescriptor(obj, 'bar');
// {set: undefined, enumerable: true, configurable: true, get: [Function: get bar]}
ds = Object.getOwnPropertyDescriptors(obj)
// { foo:
// { value: 123,
// writable: true,
// enumerable: true,
// configurable: true },
// bar:
// { get: [Function: get bar],
// set: undefined,
// enumerable: true,
// configurable: true }
// }
2、polyfill
function getOwnPropertyDescriptors(obj) {
const result = {};
for (let key of Reflect.ownKeys(obj)) {
result[key] = Object.getOwnPropertyDescriptor(obj, key);
}
return result;
}
3、缘起
该方法的引入目的,主要是为了解决Object.assign()
无法正确拷贝get
属性和set
属性的问题。 这是因为Object.assign
方法总是拷贝一个属性的值,而不会拷贝它背后的赋值方法或取值方法。
Object.getOwnPropertyDescriptors()
方法配合Object.defineProperties()
方法,就可以实现正确拷贝
我们可以看到使用 Object.assign() target1 的 foo 是 undefined,而使用 Object.getOwnPropertyDescriptors()
配合就可以把其 set 方法也拷贝了。
四、__proto__属性,Object.setPrototypeOf(),Object.getPrototypeOf()
JavaScript 语言的对象继承是通过原型链实现的。ES6 提供了更多原型对象的操作方法。
1、__proto__属性(前后各两个下划线),用来读取或设置当前对象的原型对象(prototype)
目前,所有浏览器(包括 IE11)都部署了这个属性。该属性没有写入 ES6 的正文,而是写入了附录,原因是__proto__前后的双下划线,说明它本质上是一个内部属性,而不是一个正式的对外的 API,只是由于浏览器广泛支持,才被加入了 ES6。标准明确规定,只有浏览器必须部署这个属性,其他运行环境不一定需要部署,而且新的代码最好认为这个属性是不存在的。
因此,无论从语义的角度,还是从兼容性的角度,都不要使用这个属性,而是使用下面的操作代替:Object.setPrototypeOf()(写操作)、Object.getPrototypeOf()(读操作)、Object.create()(生成操作)
2、Object.setPrototypeOf()
Object.setPrototypeOf
方法的作用与__proto__
相同,用来设置一个对象的原型对象(prototype),返回参数对象本身。它是 ES6 正式推荐的设置原型对象的方法。
Object.setPrototypeOf(object, prototype)
// object:要设置其原型的对象;prototype:该对象的新原型(一个对象 或 null)
// 等同下面
function setPrototypeOf(obj, proto) {
obj.__proto__ = proto;
return obj;
}
// 用法
let proto = {};
let obj = { x: 10 };
Object.setPrototypeOf(obj, proto);
// proto对象设为obj对象的原型,所以从obj对象可以读取proto对象的属性
proto.y = 20;
proto.z = 40;
obj.x // 10
obj.y // 20
obj.z // 40
如果第一个参数不是对象,会自动转为对象。但是由于返回的还是第一个参数,所以这个操作不会产生任何效果。
由于undefined
和null
无法转为对象,所以如果第一个参数是undefined
或null
,就会报错。
Object.setPrototypeOf(1, {}) === 1 // true
Object.setPrototypeOf('foo', {}) === 'foo' // true
Object.setPrototypeOf(true, {}) === true // true
Object.setPrototypeOf(undefined, {})
// TypeError: Object.setPrototypeOf called on null or undefined
Object.setPrototypeOf(null, {})
// TypeError: Object.setPrototypeOf called on null or undefined
3、Object.getPrototypeOf()
该方法用于读取一个对象的原型对象。常与与Object.setPrototypeOf
方法配套。
注意:如果参数不是对象,会被自动转为对象。如果参数是undefined
或null
,它们无法转为对象,所以会报错
Object.getPrototypeOf(1) === Number.prototype // true
Object.getPrototypeOf('foo') === String.prototype // true
Object.getPrototypeOf(true) === Boolean.prototype // true
// 如果参数是undefined或null,它们无法转为对象,所以会报错。
Object.getPrototypeOf(null) // TypeError: Cannot convert undefined or null to object
Object.getPrototypeOf(undefined) // TypeError: Cannot convert undefined or null to object
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律