美文网首页
深入理解ES6:4.扩展对象的功能性

深入理解ES6:4.扩展对象的功能性

作者: 独木舟的木 | 来源:发表于2019-11-28 09:54 被阅读0次

    Tips:简化属性语法、可计算属性名、简化对象方法语法、重复属性名校验、Object.is()Object.assign()、自有属性枚举、Object.setPrototypeOf()super

    对象字面量语法扩展

    属性初始值的简写

    // createPerson() 函数创建了一个对象,对象的属性名称与函数的参数名称相同
    function createPerson(name, age) {
      return {
        name: name,
        age: age
      };
    }
    
    // 在 ES6 中,当一个对象的属性与本地变量同名时,不必再写冒号和值,简单地只写属性名即可
    function createPerson(name, age) {
      return {
        name,
        age
      };
    }
    

    对象方法的简写

    // 为对象添加方法时,需要指定名称并完整定义函数来实现
    var perosn = {
      name: "Andy",
      sayName: function () {
        console.log(this.name);
      }
    }
    
    // 在 ES6 中,消除了冒号和 function 关键字
    // 在 person 对象中创建了一个 sayName() 方法
    var perosn = {
      name: "Andy",
      sayName() {
        console.log(this.name);
      }
    }
    

    可计算属性名

    // 如果想要通过计算得到属性名,需要用方括号代替点语法
    var person = {
      // 在对象字面量中,可以直接使用字符串字面量作为属性名称
      'first name': 'Nicholas'
    };
    
    // 属性名称中带有空格时,需要使用方括号
    person['first name'] = 'Andy';
    var lastName = 'last name';
    person[lastName] = 'Zakas';
    
    console.log(person['first name']); // Andy
    console.log(person[lastName]); // Zakas
    
    // 在 ES6 中,可以在对象字面量中使用可计算属性名
    // 示例一:
    let lastName = 'last name';
    
    let person = {
      'first name': 'Nicholas',
      [lastName]: 'Zakas'
    };
    
    console.log(person['first name']); // Nicholas
    console.log(person[lastName]); // Zakas
    
    // 示例二:
    var suffix = ' name';
    
    // 使用 [] 表示的属性名称是可计算的
    let person = {
      ['first' + suffix]: 'Nicholas',
      ['last' + suffix]: 'Zakas'
    };
    
    console.log(person['first name']); // Nicholas
    console.log(person[lastName]); // Zakas
    

    新增方法

    Object.is() 方法

    Object.is() 方法用来弥补全等运算符的不准确运算。这个方法接受两个参数,如果这两个参数类型相同且具有相同的值,则返回 true

    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(Object.is(5, 5)); // true
    
    console.log(5 == '5');          // true
    console.log(5 === '5');         // false
    console.log(Object.is(5, '5')); // false
    

    Object.assign() 方法

    混合(Mixin)是 JavaScript 中实现对象组合的流行方式。

    Object.assign() 方法原生实现了很多第三方库的混合(Mixin)方法。

    访问器属性:Object.assign() 方法不能将提供者的访问器属性(Getter)复制到接收对象中。

    // mixin() 函数遍历 supplier 的自有属性并复制到 receiver 中
    // 结果:receiver 不通过继承就可以获得新属性
    function mixin(receiver, supplier) {
      Object.keys(supplier).forEach(function(key) {
        receiver[key] = supplier[key];
      });
      return receiver;
    }
    
    function EventTarget() { /*... */ }
    EventTarget.prototype = {
      constructor: EventTarget,
      emit: function() { /*... */ },
      on: function() { /*... */ }
    };
    
    /*
      通过调用 mixin() 方法,
      myObject 可以接收 EventTarget.prototype 对象的所有行为,
      从而使 myObject 可以分别通过 emit() 方法发布事件或通过 on() 方法订阅事件。
    */
    var myObject = {};
    mixin(myObject, EventTarget.prototype);
    
    myObject.emit('somethingChanged');
    

    在 ES6 中,通过添加 Object.assign() 方法实现了相同的功能

    function EventTarget() { /*... */ }
    EventTarget.prototype = {
      constructor: EventTarget,
      emit: function() { /*... */ },
      on: function() { /*... */ }
    };
    
    // 在 ES6 中,通过添加 Object.assign() 方法实现了相同的功能
    var myObject = {};
    Object.assign(myObject, EventTarget.prototype);
    
    myObject.emit('somethingChanged');
    

    Object.assign() 方法可以接受任意数量的源对象,并按指定的顺序将属性复制到接收对象中。所以如果多个源对象具有同名属性,则排位靠后的源对象会覆盖排位靠前的。

    var receiver = {};
    
    Object.assign(
      receiver,
      {
        // 源对象 1
        type: 'js',
        name: 'file.js'
      },
      { 
        // 源对象 2,后者同名属性会覆盖前者
        type: 'css'
      }
    );
    
    console.log(receiver.type); // css
    console.log(receiver.name); // file.js
    

    重复的对象字面量属性

    ECMAScript 5 严格模式中加入了对象字面量重复属性的校验,当同时存在多个同名属性时会抛出语法错误。

    在 ECMAScript 6 中,重复属性检查被移除了,无论是在严格模式还是非严格模式下,代码不再检查重复属性,对于每一组重复属性,都会选取最后一个取值,就像这样:

    'use strict';
    
    var person = {
      name: 'Nicholas',
      name: 'Grey' // ES5 严格模式会有语法错误,ES6 下不会。
    };
    
    console.log(person.name); // Grey
    

    自有属性枚举顺序

    ECMAScript 5 中未定义对象属性的枚举顺序,由 JavaScript 引擎厂商自行决定。

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

    自有属性枚举顺序的基本规则是:

    1. 所有数字键按升序排序。
    2. 所有字符串键按照它们被加入对象的顺序排序。
    3. 所有 symbol 键按照它们被加入对象的顺序排序。
    var 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() 方法创建对象,其原型是在对象被创建时指定的。对象原型在实例化之后保持不变。

    虽然在 ECMAScript 5 中添加了 Object.getPrototypeOf() 来返回任意对象的原型,但仍然无法在对象被实例化之后改变其原型。

    ECMAScript 6 添加了 Object.setPrototypeOf() 方法,可以改变任意指定对象的原型。

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

    let person = {
      getGreeting() {
        return 'Hello';
      }
    };
    
    let dog = {
      getGreeting() {
        return 'Woof';
      }
    };
    
    // 以 Person 对象为原型
    // friend -> person
    let friend = Object.create(person);
    console.log(friend.getGreeting()); // Hello
    console.log(Object.getPrototypeOf(friend) === person); // true
    
    // 将原型设置为 dog
    // friend -> dog
    Object.setPrototypeOf(friend, dog);
    console.log(friend.getGreeting()); // Woof
    console.log(Object.getPrototypeOf(friend) === person); // false
    console.log(Object.getPrototypeOf(friend) === dog); // true
    

    简化原型访问的 Super 引用

    ECMAScript 6 添加了 Super 引用特性,可以更便捷地访问对象原型。

    必须在使用简写方法(对象方法的简写)的对象中使用 super 引用。

    Super 引用在多重继承下非常有用。

    let person = {
      getGreeting() {
        return 'Hello';
      }
    };
    
    let dog = {
      getGreeting() {
        return 'Woof';
      }
    };
    
    /*
     friend 对象的 getGreeting() 方法调用了同名的原型方法。
     Object.getPrototypeOf() 方法可以确保调用正确的原型。
     call(this) 可以确保正确设置原型方法中的 this 值。
     */
    let friend = {
      getGreeting() {
        return Object.getPrototypeOf(this).getGreeting.call(this) + ', hi!';
      }
    };
    
    // 以 Person 对象为原型
    Object.setPrototypeOf(friend, person);
    console.log(friend.getGreeting()); // Hello, hi!
    console.log(Object.getPrototypeOf(friend) === person); // true
    
    // 将原型设置为 dog
    Object.setPrototypeOf(friend, dog);
    console.log(friend.getGreeting()); // Woof, hi!
    console.log(Object.getPrototypeOf(friend) === dog); // true
    

    ES 6 中的简化:

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

    正式的方法定义

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

    // person 对象有一个 getGreeting() 方法
    // getGreeting() 方法的 [[HomeObject]] 属性指向 person
    let person = {
      // 是方法
      getGreeting() {
        return 'Hello';
      }
    };
    
    // 不是方法
    function shareGreeting() {
      return 'Hello';
    }
    

    Super 的所有引用都通过 [[HomeObject]] 属性来确定后续的运行过程。

    let person = {
      getGreeting() {
        return 'Hello';
      }
    };
    
    let friend = {
      getGreeting() {
        return super.getGreeting() + ', hi!';
      }
    };
    
    // 以 Person 对象为原型
    Object.setPrototypeOf(friend, person);
    
    /*
      调用 friend.getGreeting() 方法会将 person.getGreeting() 的返回值与 ', hi!' 字符串拼接并返回。
    
      friend.getGreeting() 方法的 [[HomeObject]] 属性是 firend。
      friend 的原型是 person。
      所以 super.getGreeting() 等价于 person.getGreeting.call(this)。
    */
    console.log(friend.getGreeting()); // Hello, hi!
    

    相关文章

      网友评论

          本文标题:深入理解ES6:4.扩展对象的功能性

          本文链接:https://www.haomeiwen.com/subject/ripvwctx.html