ES6新增特性——对象的扩展

132 阅读6分钟

ES6 对对象的扩展主要有:简化对象写法(属性的简写)、属性名表达式、方法的 name 属性、属性的可枚举性和遍历、super 关键字、对象的扩展运算符、AggregateError 错误对象、Error 对象的 cause 属性、对象的新增方法。

1、属性的简写

ES6中,当对象键名与对应值名相等的时候,可以进行简写

const foo = 'bar';
const baz = {foo}  // 因为对象键名foo与上面定义的变量名(对应值名)foo相同,所以可以简写(只写对象键名)

// 等同于
const baz = {foo:foo}

方法也能够进行简写

const o = {
  method() {
    return "Hello!";
  }
};

// 等同于
const o = {
  method: function() {
    return "Hello!";
  }
}

在函数内作为返回值,也会变得方便很多

function getPoint(x = 1, y = 10) {
  return {x, y};
}

console.log(getPoint())   // {x:1, y:10}
console.log(getPoint(2,3))   // {x:2, y:3}

注意:简写的对象方法不能用作构造函数,否则会报错

const obj = {
  f() {
    this.foo = 'bar';
  }
};

new obj.f() // 报错

2、属性名表达式

在 ES5 中只能使用方法一(标识符)定义属性。
ES6 允许字面量定义对象时,用方法二(表达式)作为对象的属性名,即把表达式放在方括号内。

let lastWord = 'last word';
const a = {
  a: 'say',                 // 方法一(标识符)
  'second word': 'hello',   // 方法二(表达式)
  [lastWord]: 'world',      // 方法二(表达式),lastWord是一个变量
};

a['a']            // "say" (=a.a)
a['second word']  // "hello"
a[lastWord]       // "word"
a['last word']    // "word"

表达式还可以用于定义方法名。

let obj = {
  ['h' + 'ello']() {
    return 'hi';
  }
};

obj.hello() // hi

注意,属性名表达式与简洁表示法,不能同时使用,会报错。 注意,属性名表达式如果是一个对象,默认情况下会自动将对象转为字符串 [object Object]。

3、方法的 name 属性

函数的name属性,返回函数名。对象的方法也是函数,因此也有name属性。

const person = {
  sayName() {
    console.log('hello!');
  },
};

person.sayName.name   // "sayName"

4、super关键字

this 关键字总是指向函数所在的当前对象,ES6 又新增了另一个类似的关键字 super,指向当前对象的原型对象

5、属性的可枚举性和遍历

(1)可枚举性

对象的每个属性都有一个描述对象,用来控制该属性的行为。Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。

let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, 'foo')

//  {
//    value: 123,
//    writable: true,
//    enumerable: true,
//    configurable: true
//  }

描述对象的enumerable属性,称为“可枚举性”,如果该属性为false,就表示某些操作会忽略当前属性。

目前,有四个操作会忽略enumerablefalse的属性。

  • for...in循环:只遍历对象自身的和继承的可枚举的属性。
  • Object.keys():返回对象自身的所有可枚举的属性的键名。
  • JSON.stringify():只串行化对象自身的可枚举的属性。
  • Object.assign(): 忽略enumerablefalse的属性,只拷贝对象自身的可枚举的属性。

这四个操作之中,前三个是 ES5 就有的,最后一个Object.assign()是 ES6 新增的。

(2)属性的遍历

ES6 一共有 5 种方法可以遍历对象的属性:

  • for...in:循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)

  • Object.keys(obj):返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名

  • Object.getOwnPropertyNames(obj):返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名

  • Object.getOwnPropertySymbols(obj):返回一个数组,包含对象自身的所有 Symbol 属性的键名

  • Reflect.ownKeys(obj):返回一个数组,包含对象自身的(不含继承的)所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举

上述遍历,都遵守同样的属性遍历的次序规则:

  • 首先遍历所有数值键,按照数值升序排列。
  • 其次遍历所有字符串键,按照加入时间升序排列。
  • 最后遍历所有 Symbol 键,按照加入时间升序排列。

6、扩展运算符的应用

扩展运算符...的详细介绍。ES2018 将这个运算符引入了对象。

(1)解构赋值

  对象的解构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }

注意:解构赋值必须是最后一个参数,否则会报错。

(2)扩展运算符

  对象的扩展运算符...用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。对象的扩展运算符是浅拷贝,等同于使用Object.assign()方法。

7、对象新增的方法

(1)Object.is()

严格判断两个值是否相等,与严格比较运算符(===)的行为基本一致,不同之处只有两个:+0不等于-0,NaN等于自身。

+0 === -0 //true
NaN === NaN // false

Object.is(+0, -0) // false
Object.is(NaN, NaN) // true

(2)Object.assign()

Object.assign() 方法的第一个参数是目标对象,后面的参数都是源对象。

const target = { a: 1, b: 1 };

const source1 = { b: 2, c: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

注意:Object.assign() 方法是浅拷贝,遇到同名属性会进行替换。

Object.assign() 方法的用途

  1. 为对象添加属性或方法
  2. 克隆对象
  3. 合并多个对象
  4. 为属性指定默认值

(3)Object.getOwnPropertyDescriptors()

返回指定对象所有自身属性(非继承属性)的描述对象。

const obj = {
  foo: 123,
  get bar() { return 'abc' }
};

Object.getOwnPropertyDescriptors(obj)
// { foo:
//    { value: 123,
//      writable: true,
//      enumerable: true,
//      configurable: true 
//    },
//   bar:
//    { get: [Function: get bar],
//      set: undefined,
//      enumerable: true,
//      configurable: true 
//    } 
// }

(4)Object.setPrototypeOf(),Object.getPrototypeOf()

Object.setPrototypeOf() 方法,用来设置一个对象的原型对象.

let proto = {};
let obj = { x: 10 };
Object.setPrototypeOf(obj, proto);

proto.y = 20;
proto.z = 40;

obj.x // 10
obj.y // 20
obj.z // 40

Object.setPrototypeOf() 方法,用于读取一个对象的原型对象。

function Rectangle() {
  // ...
}

const rec = new Rectangle();

Object.getPrototypeOf(rec) === Rectangle.prototype   // true

Object.setPrototypeOf(rec, Object.prototype);
Object.getPrototypeOf(rec) === Rectangle.prototype   // false

(5)Object.keys(),Object.values(),Object.entries()

ES2017 引入了跟Object.keys配套的Object.valuesObject.entries,作为遍历一个对象的补充手段,供for...of循环使用。

Object.keys 方法,返回参数对象自身的(不含继承的)所有可遍历属性的键名的数组。

var obj = { foo: 'bar', age: 42 };
Object.keys(obj)
// ["foo", "age"]

Object.values 方法,返回参数对象自身的(不含继承的)所有可遍历属性的键值的数组。

const obj = { foo: 'bar', age: 42 };
Object.values(obj)
// ["bar", 42]

Object.entries() 方法,返回参数对象自身的(不含继承的)所有可遍历属性的键值对的数组。

const obj = { foo: 'bar', age: 42 };
Object.entries(obj)
// [ ["foo", "bar"], ["age", 42] ]

(6)Object.fromEntries()

用于将一个键值对数组转为对象。

Object.fromEntries( [ ['foo', 'bar'], ['age', 42] ] )
// { foo: "bar", age: 42 }

(7)Object.hasOwn()

JavaScript 对象的属性分成两种:自身的属性和继承的属性。对象实例有一个hasOwnProperty()方法,可以判断某个属性是否为原生属性。ES2022 在Object对象上面新增了一个静态方法Object.hasOwn(),也可以判断是否为自身的属性。

Object.hasOwn()可以接受两个参数,第一个是所要判断的对象,第二个是属性名。

const foo = Object.create({ a: 123 });
foo.b = 456;

Object.hasOwn(foo, 'a') // false
Object.hasOwn(foo, 'b') // true

上面示例中,对象foo的属性a是继承属性,属性b是原生属性。Object.hasOwn()对属性a返回false,对属性b返回true

Object.hasOwn() 的一个好处是,对于不继承Object.prototype的对象不会报错,而hasOwnProperty()是会报错的。

const obj = Object.create(null);

obj.hasOwnProperty('foo') // 报错
Object.hasOwn(obj, 'foo') // false

上面示例中,Object.create(null)返回的对象obj是没有原型的,不继承任何属性,这导致调用obj.hasOwnProperty()会报错,但是Object.hasOwn()就能正确处理这种情况。