《Understanding ES6》 chapter4 Expanded Object Functionality

227 阅读3分钟

2019-6-19

第四章 扩展的对象功能

(一)对象字面量

属性初始化器的速记法

当对象的属性名称与本地变量名称相同时,可以只书写名称,省略冒号和值

let name = "xiaoming",
		age = 30,
		obj = {
			name,   //非省略写法 name: name
			age			//非省略写法 age: age
		}

方法简写

方法简写能使用super,非简写的方法则不能

var person = {
	name: 'xiaoming',
	sayName() {   //非省略写法 say: function() {}
		console.log(this.name);
	}
}

可计算属性名

[]表示法允许将任意字符串用作属性名,可用于对象实例和对象字面量中

var person = {},
    lastName = "last name";
// 用于实例
person["first name"] = 'Nicholes';
person[lastName] = 'Zakas';

//用于对象字面量
person = {
  "first Name": 'Nicholes',
  [lastName]: 'Zakas'
};

var suffix = " name";

var person = {
    ["first" + suffix]: "Nicholas",
    ["last" + suffix]: "Zakas"
};

(二)新的方法

Object.is()

弥补 ”===“ 认为+0与-0相等的缺陷

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

Object.assign(receive,source1, source2...)

参数:接受一个接收者对象,和任意个源对象,将源对象上的属性浅复制到接收者对象上,然后返回接收者对象。

//Object.assign()的实现源码
function mixin(receiver, supplier) {
    Object.keys(supplier).forEach(function(key) {
        receiver[key] = supplier[key];
    });

    return receiver;
}

**注意:**不能将源对象的访问器属性复制给接收者对象,因为使用了赋值运算符,源对象的访问器属性会转变为接收对象的数据属性

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

(三)重复的对象字面量属性

无论是否在严格模式,ES6都不再检查重复的对象字面量属性,当存在重复属性时,后面的会覆盖前面的。

"use strict";

var person = {
    name: "Nicholas",
    name: "Greg"        // no error in ES6 strict mode
};

console.log(person.name);       // "Greg"

(四) 自有属性的枚举顺序

  1. 数字 =》 升序
  2. 字符串、符号 =》 按添加顺序

在对象字面量中定义的属性会首先出现,然后才是动态添加到对象中的键

    var obj = {
    a: 1,
    0: 1,
    c: 1,
    2: 1,
    b: 1,
    1: 1,
    e: 1
};

obj.d = 1;

console.log(Object.getOwnPropertyNames(obj).join(""));     // "012acbed"

(五) 原型

  1. 修改原型

Object.create(); 创建对象时指定原型

Object.getPrototypeOf(); 获取对象原型

Object.setPrototypeOf(); 修改对象原型

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

let dog = {
    getGreeting() {
        return "Woof";
    }
};

// prototype is person
let friend = Object.create(person);
console.log(friend.getGreeting());                      // "Hello"
console.log(Object.getPrototypeOf(friend) === person);  // true

// set prototype to dog
Object.setPrototypeOf(friend, dog);
console.log(friend.getGreeting());                      // "Woof"
console.log(Object.getPrototypeOf(friend) === dog);     // true
  1. 使用super引用

super 是指向当前对象的原型的一个指针,可以使用super来调用对象原型上的任何方法,只要这个引用是位于简写的方法之内。

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

let friend = {
    getGreeting() {
      // 在这里super.getGreeting 等价于  Object.getPrototypeOf(this).getGreeting.call(this)
		  // return Object.getPrototypeOf(this).getGreeting.call(this) + ", hi!";
      return super.getGreeting() + ", hi!";
    }
};

// set prototype to person
Object.setPrototypeOf(friend, person);
console.log(friend.getGreeting());                      // "Hello, hi!"
console.log(Object.getPrototypeOf(friend) === person);  // true


// 不在简写的方法内引用super,会导致语法错误
let friend = {
    getGreeting: function() {
        // 语法错误
        return super.getGreeting() + ", hi!";
    }
};

super引用不是动态的,所以它总能指向正确的对象,可以轻松实现多级继承

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

// prototype is person
let friend = {
    getGreeting() {
        return Object.getPrototypeOf(this).getGreeting.call(this) + ", hi!";
    }
};
Object.setPrototypeOf(friend, person);


// prototype is friend
let relative = Object.create(friend);

console.log(person.getGreeting());                  // "Hello"
console.log(friend.getGreeting());                  // "Hello, hi!"
//relative的原型是friend, friend.getGreeting().call()调用会导致进程开始反复进行递归调用,直至发生堆栈错误
console.log(relative.getGreeting());                // error! 

// prototype is person
let friend = {
    getGreeting() {
      // super是非动态的,这里的super.getGreeting始终指向person.getGreeting()
        return super.getGreeting() + ", hi!";
    }
};
Object.setPrototypeOf(friend, person);


// prototype is 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]]**内部属性的函数,[[HomeObject]]指向该方法所属的对象。

let person = {
    // method
    getGreeting() { // [[HomeObject]]指向person
        return "Hello";
    }
};

// not a method
function shareGreeting() { // 无[[HomeObject]]属性
    return "Hi!";
}

任何对super的引用都会使用[[HomeObject]]属性来判断要做什么:

  1. 在[[HomeObject]]上调用Object.getPrototypeOf()来获取对原型的引用;
  2. 在该原型上查找同名函数;
  3. 创建this绑定并调用该方法。