深入理解ES6 笔记四:扩展对象的功能性

118 阅读5分钟

对象字面量的语法扩展

属性初始值的简写

在ES6中,当一个对象的属性与本地变量同名时,不必再写冒号和值,简单的只写属性名即可。当对象字面量里只有一个属性的名称时,JavaScript引擎会在可访问作用域中查找其同名变量;如果找到,则该变量的值被赋给对象字面量里的同名属性。

function getPerson(name, age) {
    return {
        name: name,
        age: age
    }
}
// 简写↓
function getPerson(name, age) {
    return {
        name,
        age
    }
}

对象方法的简写语法

消除了冒号和function关键字。

const person = {
    name: 'apple',
    sayName: function() {
        console.log(this.name);
    }
}
// 简写↓
const person = {
    name: 'apple',
    sayName() {
        console.log(this.name);
    }
}

两者唯一的区别是:简写方法可以使用super关键字

可计算属性名

在ES5,如果想要通过计算得到属性名,就需要用方括号代替点记法。此外,在对象字面量中,可以直接使用子符串字面量作为属性的名称。
在ES6,可在对象字面量中使用可计算属性名称,其语法与引用对象实例的可计算属性名称相同,也是使用方括号。同样可以使用表达式作为属性的可计算名称。

// ES5
const person {
    'first name': 'first name'
},
lastName = 'last Name'

person['second name'] = 'second name';
person[lastName] = 'last name';
console.log(person);
/*{
    'first name': 'first name',
    'second name': 'second name',
    'last name' = 'last name'
}*/

//ES6 ↓
const lastName = 'last Name',
    name = 'name';
const person {
    'first name': 'first name',
    ['midle' + name]: 'middle name'
    [lastName]: 'last name',
};

console.log(person);
/*{
    'first name': 'first name',
    'second name': 'second name',
    'last name' = 'last name'
}*/

新增方法

Object.is()方法

ES6引入Object.is()方法来补全全等运算符的不准确运算。这个方法接受两个参数,如果这两个参数类型相同且具有相同的值,则返回true。
是否选择用Object.is()方法而不是==或===取决于那些特殊情况如何影响代码。

console.log(+0 == -0);              // true
console.log(+0 === -0);             // true
console.log(Object.is(+0, -0));     // false

console.log(NaN == NaN);              // false
console.log(NaN === NaN);             // false
console.log(Object.is(NaN, NaN));     // true

console.log(5 == 5);                // true
console.log(5 == '5');              // true
console.log(5 === 5);               // true
console.log(5 === '5');             // false
console.log(Object.is(5, 5));       // true
console.log(Object.is(5, '5'));     // false

Object.assign()方法

ES6添加了Object.assign()方法来实现一个对象接受另一个对象的属性和方法,这个方法接受一个接收对象和任意数量的源对象,并按制定顺序将属性赋值到接收对象中,如果多个源对象具有同名属性,则排位靠后的源对象会覆盖排位靠前的,最终返回接收对象。

function EventTarget(){/*...*/}
EventTarget.prototype = {
    constructor: EventTarget,
    emit: function(){/*...*/},
    on: function(){/*...*/}
}

const myObj = {};
Object.assign(myObj, EventTarget.prototype);
console.log(myObj.emit());

const receiver = {};
Object.assign(receiver, {
    type: 'js',
    name: 'file.js'
}, {
    type: 'css'
});
console.log(receiver.type); // css

重复的字面量属性

ES6中重复属性检车被移除了,代码不在检查重复属性,对每一组重复属性,都会选取最后一个取值。

const person = {
    name: 'apple',
    name: 'banana'
}
console.log(person.name);   // banana

自由属性枚举顺序

ES6严格规定了对象的自有属性被枚举时的返回顺序,这会影响到Object.getOwnPropertyNames()方法以及Reflect.ownKeys返回属性的方式,Object.assign方法的处理顺序也将随之改变。
自有属性枚举顺序的基本规则是:

  1. 所有数字键按升序排序
  2. 所有字符串键按它们被加入对象的顺序排序
  3. 所有Symbol键按照它们被加入对象的顺序排序
const obj = {
    a: 1,
    0: 1,
    c: 1,
    2: 1,
    b: 1,
    1: 1
}
obj.d = 1;
console.log(Object.getOwnPropertyNames(obj).join(''));  // '012acbd'

增强对象的原型

改变对象的原型

正常情况下,无论是通过构造函数还是Object.create()方法创建对象,其原型是在对象被创建是制定的。对象原型在实例化后保持不变。ES6添加了Object.setProrotyprOf()方法来改变这一现状,通过这个方法可以改变任意制定对象的原型,它接受两个参数:被改变原型的对象以及替代第一个参数原型的对象。

let person = {
    getGreeting() {
        return 'Hello';
    }
}
let dog = {
    getGreeting() {
        return 'Woof';
    }
}

// 以person对象为原型
let friend = Object.create(person);
console.log(friend.getGreeting());                      // 'Hello'
console.log(Object.getPrototyeOf(friend) === person);   // true

//将原型设置为dog
Object.setPrototyeOf(friend, dog);
console.log(friend.getGreeting());                      // 'Woof'
console.log(Object.getPrototyeOf(friend) === dog);   // true

对象原型的真实值被储存在内部专用属性[[Protrtype]]中,调用Object.getPrototyeOf()方法返回存储其中的值,调用Object.setPrototyeOf()方法改变其中的值。

简化原型访问的Super引用

ES6引入了Super引用的特性,使用它可以更便捷地访问对象原型。Super引用相当于指向对象原型的指针,也就是Object.getPrototypeOf(this)的值。可以通过Super引用调用对象原型上多有其他方法,必须要在使用简写方法的对象中使用Super引用。此时的this绑定会被自动设置为当前作用域的this值

let person = {
    getGreeting() {
        // Object.getPrototypeOf(this).getGreeting().call(this)相同
        return super.getGreeting() + ', Hi!';
    },
    getGreeting2: function() {
        // 语法错误
        return super.getGreeting() + ', Hi!';
    }
}

Super引用在多重继承的情况下非常有用。Super的引用不是动态变化的,它总是指向正确的对象。

let person = {
    getGreeting() {
        return 'Hello';
    }
}

// 以person为原型
let friend = {
    getGreeting() {
        return super.getGreeting() + ', Hi!';
    },
}

Object.setPrototypeOf(friend, person);

// 原型是friend
let relative = Object.create(friend);

console.log(person.getGreeting());      // Hello
console.log(friend.getGreeting());      // Hello, Hi!
console.log(relative.getGreeting());    // Hello, Hi!

正确的方法定义

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

let person = {
    // 是方法
    getGreeting() {
        return 'Hello';
    }
}

// 不是方法
function getGreeting() {
    return 'Hi';
}

Super的所有引用都是通过[[HomeObject]]属性来确定后续的运行过程。第一步是在[[HomeObject]]属性上调用Object.getPrototypeOf()方法来检索原型的引用;然后搜寻原型找到同名函数;最后,设置this(当前作用域)绑定并且调用相应的方法。