深入理解ES6-4.对象扩展

50 阅读3分钟

对象字面量简化

  1. 属性名和变量名同名,可只写属性名
  2. 方法可省略 : 和 function 关键字
  3. 可计算的属性名,在对象字面量中使用方括号表示属性名称是可计算的
let name = "Nicholas";
let person2 = {
    name,
    sayName() {
        console.log(this.name);
    },
    ["first name"] : "nicol1"
}
  1. 可计算的属性名,在对象字面量中使用方括号表示属性名称是可计算的

新增对象方法

  1. Object.is() :弥补全等运算符的不准确运算。
  2. Object.assign() :浅拷贝,不能复制访问器属性。

增强对象原型

改变对象的原型

es6新增了 Object.setPrototypeOf() 方法用于改变对象的原型。

Super

先让我们使用es6之前的语法实现一个功能:在实例中重写原型的方法,但其中要调用与它同名的原型方法。

let person = {
    name:"Hello",
    getGreeting() { return this.name + ' person'; }
};
let dog = {
    name:"Woof",
    getGreeting() { return this.name + ' dog'; }
};
//重写实例的方法,同时调用原型的方法
let friend = {
    name:"yo",
    getGreeting() {
        // Object.getPrototypeOf(this)就是实例的原型
        // getGreeting.call(this) call用于将方法中this的值绑定到实例上
        return Object.getPrototypeOf(this).getGreeting.call(this) + ", hi!";
    }
};

Object.setPrototypeOf(friend , person); // 将原型设置为person
friend.getGreeting();   //"yo person, hi! "

Object.setPrototypeOf(friend, dog);   // 将原型设置为dog
friend.getGreeting();    // "yo dog, hi! "

es6提供 super 关键字,指向对象的原型,相当于 Object.getPrototypeOf(this),所以上面的代码可以改为:

--    return Object.getPrototypeOf(this).getGreeting.call(this) + ", hi!";
++    return super.getGreeting() + ", hi!";

可以看出使用 super 关键字,可以调用到原型上(其实也就是原型链继承上的父类)的方法,但是其内部 this 的指向仍是实例对象。

Object.getPrototypeOf() 在多重继承下会出现问题,super 也可以很好的避免这个问题,因为 super 引用不是动态变化的,它总是指向正确的对象。(Object.create()原型式继承

let person = {
    getGreeting() { return "Hello"; }
};
let friend = {
    getGreeting() { 
       return Object.getPrototypeOf(this).getGreeting.call(this) + ", hi!"; 
       // return super.getGreeting() + ", hi!"; 可以正确运行
    }
};
Object.setPrototypeOf(friend, person);
// 原型式继承
let relative = Object.create(friend);
//产生递归调用 Uncaught RangeError: Maximum call stack size exceeded
relative.getGreeting();
  1. 当执行 relative.getGreeting() 时,由于原型式继承,所以会沿着原型链向上查找,执行 friend 中的 getGreeting() 方法。
  2. 执行的是 Object.getPrototypeOf(relative).getGreeting.call(relative) + ", hi!",也可以简化为 friend.getGreeting.call(relative) + ", hi!"
  3. 这里再次调用 friend 中的 getGreeting() 方法,但是由于 callthis 值绑定到了 relative,所以还是执行 Object.getPrototypeOf(relative).getGreeting.call(relative) ,因此产生了循环调用。

正式的方法定义

es6正式将对象中的简写形式的函数定义为方法。方法具有一个 [[HomObject]] 属性,值为包含这个方法的对象。

let person = {
    // 这个方法的 [[HomObject]] 属性值就是 person
    getGreeting() { return "Hello"; }
};
let friend = {
    getGreeting() { return super.getGreeting() + ", hi!"; }
};
Object.setPrototypeOf(friend, person);
friend.getGreeting()

当我们使用 super 关键的字的时候,实际执行的操作是:

  1. 找到当前对象的 [[HomObject]] 属性,使用 Object.getPrototypeOf() 方法找到这个属性值的原型。
  2. 找到原型上的方法后,设置 this 绑定,最后执行。
  3. 所以 super.getGreeting() 等价于 person.getGreeting().call(this)

super 只能写在对象字面量中简写的方法(省略 : 和 function 关键字 )里。因为super关键字设计的时候就是专为对象中的方法提供的,所以需要通过对象中方法可以简写的特性来区分一个方法是否为对象中的方法。