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()
方法处理属性的顺序也将随之改变。
自有属性枚举顺序的基本规则是:
- 所有数字键按升序排序。
- 所有字符串键按照它们被加入对象的顺序排序。
- 所有
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!