浅析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
复制代码

  如果第一个参数不是对象,会自动转为对象。但是由于返回的还是第一个参数,所以这个操作不会产生任何效果。

  由于undefinednull无法转为对象,所以如果第一个参数是undefinednull,就会报错。

复制代码
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方法配套。

  注意:如果参数不是对象,会被自动转为对象。如果参数是undefinednull,它们无法转为对象,所以会报错

复制代码
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
复制代码

 

posted @   古兰精  阅读(632)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示