四: ES6 对象扩展

作者: 岁月静好_不负此生 | 来源:发表于2019-12-26 20:19 被阅读0次

前言

该部分为书籍 深入理解ES6 第四章(扩展的对象功能)笔记

对象类别

对象类别包括:

  1. 普通对象: 拥有 JS 对象所有默认的内部行为
  2. 奇异对象: 其内部行为在某些方面有别于默认行为
  3. 标准对象: 在 ES6中被定义的对象, 例如 ArrayDate 等等. 标准对象可以是普通的, 也可以是奇异的
  4. 内置对象: 在脚本开始运行时由 JS 运行环境提供的对象. 所有的标准对象都是内置对象

对象字面量语法的扩展

  1. 当对象字面量中的属性只有名称时, JS 引擎会在周边作用域查找同名变量

    function createPerson(name, age) {
     return {
         name,
         age
     };
    }
    
  2. 方法简写: 可以省略冒号与 function 关键字

    同 ES5 方法的区别: 方法简写能使用 super, 而非简写的方法则不能使用.

    var person = {
     name: "Nicholas",
        // 这个也是标准函数, 不是箭头函数
     sayName() {
         console.log(this.name);
     }
    };
    
  3. 需计算属性名

    • 在 ES5 中, 方括号允许你将变量或字符串字面量指定为属性名,而在字符串中允许存在作为标识符时会导致语法错误的特殊字符。

      缺陷: 不能在对象属性中使用变量, 或者通过计算才能获得属性的

      var person = {},
       lastName = "last name";
      
      person["first name"] = "Nicholas";
      person[lastName] = "Zakas";
      
      console.log(person["first name"]); // "Nicholas"
      console.log(person[lastName]); // "Zakas"
      
      // === ES5 中不允许的 ===
      var person = {
          [lastName]:'Zakas'
      }
      person["first" + suffix];
      
    • 在 ES6 中改善了其计算属性的缺陷

      var lastName = "last name";
      var suffix = " name";
      
      var person = {
       "first name": "Nicholas",
       [lastName]: "Zakas",
          ["first" + suffix]: "Nicholas",
      };
      
      console.log(person["first name"]); // "Nicholas"
      console.log(person[lastName]); // "Zakas"
      

新增方法

ES 从 ES5 开始就有一个设计意图:避免创建新的全局函数,避免在 Object 对象的原型上
添加新方法,而是尝试寻找哪些对象应该被添加新方法。因此,对其他对象不适用的新方法
就被添加到全局的 Object 对象上。

  1. Object.is()方法

    严格相等运算符(===) 会认为 +0 与 -0 相等, 也会认为 NaN === NaN 会返回 false.

    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
    
  2. Object.assign() 方法: 混入, 一个对象会从另一个对象中接收属性与方法

    由于方法内部是使用 赋值运算符(=)的, 它就无法将访问属性复制到接受者上(会调用访问器属性的 get 的方法)

    var receiver = {};
    Object.assign(receiver,
     {
         type: "js",
         name: "file.js"
     },
        // 同属性, 后面的会覆盖前面的
     {
         type: "css"
     }
    );
    console.log(receiver.type); // "css"
    console.log(receiver.name); // "file.js"
    
    // === 供应者的访问器属性就会转变成接收者的数据属性 ===
    var receiver = {},
    supplier = {
     get name() {
         return "file.js"
     }
    };
    
    Object.assign(receiver, supplier);
    
    var descriptor = Object.getOwnPropertyDescriptor(receiver, "name");
    
    console.log(descriptor.value); // "file.js"
    console.log(descriptor.get); // undefined
    

重复的对象字面量属性

  1. 在 ES5 的严格模式下, 为重复的对象字面量属性引入了一个检查, 若找到重复的属性名, 就会抛出错误
  2. 在 ES6 中, 不管严格模式还是非严格模式, 重复定义对象字面量属性不会抛出错误,当存在重复属性时, 排在后面的属性的值会覆盖后面的值

自有属性的枚举顺序

ES5 中, 并没有定义对象属性的枚举顺序, 而在 ES6 中则严格定义了对象自有属性在被枚举时返回的顺序.

自有属性枚举时基本顺序如下:

  1. 所有的数字类型键, 按升序排列
  2. 所有的字符串类型键, 按被添加到对象的顺序排列
  3. 所有的符号类型键, 也按添加顺序排列
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.getOwnPropertyNames() 与Reflect.ownKeys (详见第十二章)如何返回属性造成了影响,还同样影响了Object.assign() 处理属性的顺序。

for-in 循环的枚举顺序仍未被明确规定,因为并非所有的 JS 引擎都采用相同的方式。
而 Object.keys() 和 JSON.stringify() 也使用了与 for-in 一样的枚举顺序。

更强大的原型

  1. 在 ES5 中, 对象的原型在初始化完成后( 通过构造器或 Object.create() 方法 )会保持不变.

  2. 在 ES6 中, 添加了 Object.setPrototypeOf() 方法, 允许修改任意指定对象的原型.

    ES6 的 Object.setPrototypeOf() 方法,现在能够在对象已被创建之后更改它的原型了。

    // 接受两个参数: 需要被修改原型的对象,以及将会成为前者原型的对象.
    let person = {
     getGreeting() {
         return "Hello";
     }
    };
    let dog = {
     getGreeting() {
         return "Woof";
     }
    };
    
    // 原型为 person
    let friend = Object.create(person);
    console.log(friend.getGreeting()); // "Hello"
    console.log(Object.getPrototypeOf(friend) === person); // true
    
  3. 在 ES5 中, 若要覆盖对象实例的一个方法、但依然要调用原型上的同名方法,可采取的方法如下:

    let person = {
     getGreeting() {
         return "Hello";
     }
    };
    
    let dog = {
     getGreeting() {
         return "Woof";
     }
    };
    
    let friend = {
     getGreeting() {
            // 采集到原型对象直接调用原型对象上的方法, 并直接调用, 使用 call 是为了保持this指针的正确
         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
    

    但是多级继承时, 此方法是不适用的

    let person = {
     getGreeting() {
         return "Hello";
     }
    };
    
    // 原型为 person
    let friend = {
         getGreeting() {
                // 这里会报错, 这是因为此时 this 的值是 relative ,而 relative 的原型是 friend 对象,这样friend.getGreeting().call() 调用就会导致进程开始反复进行递归调用,直到发生堆栈错误。
             return Object.getPrototypeOf(this).getGreeting.call(this) + ", 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()); // error!
    
  4. 在 ES6 中, super 引用是指向当前对象的原型的一个指针, 并且 super 并非是动态的, 它总是能指向正确的对象

    super 引用并不是在调用时才确定的, 而是根据对象(需要用在对象的方法简写 和 class语法中)的原型, 始终指向当前对象的原型

    不能在方法简写之外的情况使用 super, 会导致语法错误

    let person = {
     getGreeting() {
         return "Hello";
     }
    };
    
    // 原型为 person
    let friend = {
         getGreeting() {
                // 这里 super 指向的就是当前对象(friend)的原型, 并且this指针也是根据调用时的对象决定
             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!"
    

正式的"方法"定义

  1. 在 ES6之前 , 方法的概念从未被正式定义, 只是一个称呼, 此前仅指对象的函数属性(而非数据属性)

  2. 在 ES6 中, 正式做出了定义: 方法是一个拥有 [[HomeObject]] 内部属性的函数, 此内部属性指向该方法所属的对象

    任何对 super 的引用都会使用[[HomeObject]]属性来判断要做什么。第一步是在[[HomeObject]] 上调用 Object.getPrototypeOf() 来获取对原型的引用;接下来,在该原型上查找同名函数;最后,创建 this 绑定并调用该方法。

    let person = {
     // 方法
     getGreeting() {
         return "Hello";
     }
    };
    
    // 并非方法
    function shareGreeting() {
     return "Hi!";
    }
    

相关文章

  • 2018-08-24

    ES6对象的扩展

  • es6之扩展运算符 三个点(...)

    es6之扩展运算符 三个点(...)对象的扩展运算符数组的扩展运算符总结es6之扩展运算符 三个点(…)对象的扩展...

  • ES6扩展归纳

    ES6扩展归纳 介绍ES6相比与ES5在原有对象上的扩展,包括字符串、正则、数值、函数、数组、对象等扩展,本文是阮...

  • 四: ES6 对象扩展

    前言 该部分为书籍 深入理解ES6 第四章(扩展的对象功能)笔记 对象类别 对象类别包括: 普通对象: 拥有 JS...

  • es6的数值,函数,字符串扩展

    一、es6的数值扩展 二、es6的函数扩展 代码效果:对象解构在函数中的应用 三、es6字符串扩展 常用方法 代码...

  • es6笔记

    es6 对象扩展方法is() 全等比较Object.is(NaN,NaN) // trueassign() 对象合...

  • ES6新增特性(二)

    ES6 的内置对象扩展 Array 的扩展方法 一、Array 的扩展方法 1. 扩展运算符(展开语法) 扩展运算...

  • ES6学习笔记二 (扩展对象的功能性、解构、Symbol)

    第四章 扩展对象的功能性 1. 对象字面量语法扩展 直接看例子 ES6中通过属性初始化的简写语法,可以消除这种属性...

  • 【基础知识】扩展运算符...

    变量解构使用案例 数组扩展运算符 重点:对象也可以使用...哦ES6之扩展运算符-对象

  • ES6 学习(Number扩展篇)

    - Number扩展 ES6 把 parseInt() 和 parseFloat() 移植给了 Number 对象...

网友评论

    本文标题:四: ES6 对象扩展

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