ES6 扩展对象的功能性

230 阅读3分钟

对象字面量语法扩展

属性初始值的简写

当一个对象的属性与本地变量同名的时候,不必再写冒号和值,只写属性名即可。

function createFunction(name, age) {
  return {
    name,
    age
  }
}

对象方法的简写

如果为对象添加方法,则简化为消除冒号和 function 关键字。

let dog = {
  name: 'Sugary',
  // getName: function () {
  //   return this.name
  // }, // ES5中对象方法的语法
  getName() {
    return this.name
  }
}

可计算属性名

let suffix = 'name'

let person = {
  ['first' + suffix]: 'Sugary',
  ['last' + suffix]: 'Nai',
}

console.log(person['firstname']) // Sugary
console.log(person['lastname']) // Nai

新增方法

Object.is()

Object.is() 方法的出现主要是弥补全等运算符(===)的不准确运算。在 JavaScript 中,+0 和 -0 表示的是两个完全不同的实体,但用全等运算符对两者进行比较时,得到的是相同的结果;NaN === NaN 返回的结果是 false, 需要用 isNaN() 方法才可以正确检测 NaN。

console.log(+0 === -0) // true
console.log(NaN === NaN) // false

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

Object.assign()

该方法表示一个对象接收来自另一个对象的属性和方法。该方法可以接收任意数量的源对象,并按照指定的顺序将属性复制到接收对象中。如果多个源对象具有同名属性,则排位靠后的源对象会覆盖排位靠前的。

let receiver = {}

let supplier1 = {
  type: 'js',
  name: 'file.js',
}

let supplier2 = {
  type: 'css',
}

Object.assign(receiver, supplier1, supplier2)

console.log(receiver) // { type: 'css', name: 'file.js' }

自有属性枚举顺序

ES6 严格规定了自有属性被枚举时的返回顺序,返回顺序的基本规则是:

  1. 所有数字键按升序排序;
  2. 所有字符串键按照它们被加入对象的顺序排序;
  3. 所有 symbol 键按照它们被加入对象的顺序排序。
let myObj = {
  a: 1,
  0: 1,
  c: 1,
  2: 1,
  b: 1,
  1: 1,
}
myObj.d = 1

console.log(Object.getOwnPropertyNames(myObj).join('')) // 012acbd

增强对象的原型

改变对象的原型

  1. ES5 中用 Object.getPrototypeOf() 方法来返回任意指定对象的原型;
  2. ES6 中用 Object.setPrototypeOf() 方法,可以改变任意指定对象的原型。

具体方法如下:

let Person = {
  getGreeting() {
    return 'Hello'
  },
}

let Dog = {
  getGreeting() {
    return 'Woof'
  },
}

let friend = Object.create(Person)
console.log(friend.getGreeting()) // Hello
console.log(Object.getPrototypeOf(friend) === Person) // true

Object.setPrototypeOf(friend, Dog)
console.log(friend.getGreeting()) // Woof
console.log(Object.getPrototypeOf(friend) === Dog) // true

简化原型访问的 Super 引用

如果你想重写对象实例的方法,又需要调用与它同名的原型方法。在 ES5 中是这样实现的:

let Person = {
  getGreeting() {
    return 'Hello'
  },
}

let Dog = {
  getGreeting() {
    return 'Woof'
  },
}

let friend = {
  getGreeting() {
    return Object.getPrototypeOf(this).getGreeting.call(this) + ', hi!'
  },
}

Object.setPrototypeOf(friend, Person)
console.log(friend.getGreeting()) // Hello, hi!
console.log(Object.getPrototypeOf(friend) === Person) // true

Object.setPrototypeOf(friend, Dog)
console.log(friend.getGreeting()) // Woof, hi!
console.log(Object.getPrototypeOf(friend) === Dog) // true

但是这种方法在多重继承的情况下会有问题,会进入递归调用直到触发栈溢出报错。这个时候,我们可以使用 ES6 新引入的关键字 super 。super 引用相当于指向对象原型的指针。

let Person = {
  getGreeting() {
    return 'Hello'
  },
}

let Dog = {
  getGreeting() {
    return 'Woof'
  },
}

let friend = {
  getGreeting() {
    // 此处是简写方法 
    // return Object.getPrototypeOf(this).getGreeting.call(this) + ', hi!'
    return super.getGreeting() + ', hi!'
  },
}

Object.setPrototypeOf(friend, Person)
console.log(friend.getGreeting()) // Hello, hi!
console.log(Object.getPrototypeOf(friend) === Person) // true

Object.setPrototypeOf(friend, Dog)
console.log(friend.getGreeting()) // Woof, hi!
console.log(Object.getPrototypeOf(friend) === Dog) // true

注意:必须要在使用简写方法的对象中使用 Super 引用,否则会报语法错误。

正式的方法定义

ES6 中将方法定义为一个函数,它会有一个内部的 [[HomeObject]] 属性来容纳这个方法从属的对象。

let Person = {
  // 是方法
  getGreeting() {
    return 'Hello'
  },
}

// 不是方法
function shareGreeting() {
  return 'Hi!'
}

示例中的 getGreeting() 方法的[[HomeObject]] 属性值为 Person,而 shareGreeting() 是没有 [[HomeObject]] 属性的。ES6 定义这个方法主要是为 Super 服务的。Super 的所有引用都通过 [[HomeObject]] 属性来确定后续的运行过程。