四: ES6 对象扩展

前言

该部分为书籍 深入理解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!";
    }
    
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容