ES6
经过多种方式来增强对象的使用,经过简单的语法扩展,提供更多操做对象及与对象交互的方法。javascript
ES5中初始化属性值的方式:java
function createPerson(name, age) { return { name: name, age: age }; }
这段代码中的createPerson()
函数建立的对象,其属性名与函数的参数相同,在返回的结果中,name
和age
分别重复了两遍,只是其中一个是对象属性名,另外一个是为属性赋值的变量。编程
ES6
中能够简单地只写属性名便可:函数
function createPerson(name, age) { return { name, age }; }
当对象字面量里只有一个属性的名称时,JavaScript
引擎会在可访问做用域中查找同名变量;若是找到,则该变量的值被赋给对象字面量的同名属性。this
ES5中定义对象方法:prototype
var person = { name: "Nicholas", sayName: function() { console.log(this.name); } };
ES6中简写后的对象方法:指针
var person = { name: "Nicholas", sayName() { console.log(this.name); } };
两者惟一的区别是,简写方法可使用super
关键字(稍后会讨论)。code
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 = { "first name": "Nicholas" }; console.log(person["first name"]); // "Nicholas"
这种模式适用于属性名提早已知或可被字符串字面量表示的状况。然而,若是属性名"first name"被包含在一个变量中(就像以前的示例中的那样),或者须要经过计算才能获得该变量的值,那么ES5
中是没法为一个对象字面量定义该属性的。
而在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"; var person = { ["first" + suffix]: "Nicholas", ["last" + suffix]: "Zakas" }; console.log(person["first name"]); // "Nicholas" console.log(person["last name"]); // "Zakas"
当你想在JavaScript
中比较两个值时,可能习惯于使用相等运算符(==)或全等运算符(===),许多开发者更喜欢后者,从而避免在比较时触发强制类型转换的行为。但即便全等运算符也不彻底准确,好比+0和-0在JavaScript
引擎中被表示为两个彻底不一样的实体,而若是使用全等运算符===对二者进行比较,获得的结果是二者相等;一样,NaN===NaN的返回值为false,须要使用isNaN()
方法才能够正确检测NaN
。
ES6
引入了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 consolog.log(5 == 5); // true consolog.log(5 == "5"); // true consolog.log(5 === 5); // true consolog.log(5 === "5"); // false console.log(Object.is(5, 5)); // true console.log(Object.is(5, "5")); // false
对于Object.is()
方法来讲,其运行结果在大部分状况下与===运算符相同,惟一的区别在于+0和-0被识别为不相等而且NaN与NaN等价。可是你大可没必要抛弃等号运算符,是否选择使用Object.is()
方法而不是===取决于那些特殊状况如何影响代码。
混合(Mixin)是JavaScript
种实现对象组合最流行的一种模式。在一个mixin
方法中,一个对象接收来自另外一个对象的属性和方法,许多JavaScript
库中都有相似minxin
方法:
function mixin(receiver, supplier) { Object.keys(supplier).forEach(function(key) { receiver[key] = supplier[key]; }); return receiver; }
mixin()
函数遍历supplier
的自有属性并复制到receiver
中(复制行为是浅复制,当属性值为对象时只复制对象的引用)。这样一来,receiver
不经过继承就能够得到新属性,请参考这段代码:
function EventTarget() { ... } EventTarget.prototype = { constructor: EventTarget, emit: function() { ... }, on: function() { ... } }; var myObject = {}; mixin(myObject, EventTarget.prototype); myObject.emit("somethingChanged");
这种混合模式很是流行,于是ES6
添加了Object.assign()
方法来实现相同的功能:
Object.assign(myObject, EventTarget.prototype);
Object.assign()
方法接受一个接收对象和任意数量的源对象,最终返回接收对象。若是多个源对象具备相同属性,则排位靠后的会覆盖排位靠前的。
须要注意的是,Object.assign()
方法不能将提供者的访问器属性复制到接收对象中。因为Object.assign()
方法执行了赋值操做,所以提供者的访问器属性最终会转变为接收对象中的一个数据属性,请看示例:
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
在这段代码中,supplier
有一个名为name的访问器属性。当调用Object.assign()
方法时返回字符串"file.js",所以receiver
接收这个字符串后将其存为数据属性receiver.name
。
正常状况下,不管是经过构造函数仍是Object.create()
方法建立对象,其原型是在被建立时指定的。对象原型在实例化以后保持不变,直到ES5都是JavaScript
编程最重要的设定之一,虽然在ES5
中添加了Object.getPrototypeOf()
方法来返回任意指定对象的原型,但仍缺乏对象在实例化后改变原型的标准方法。
因此,在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 // 将原型设置为dog console.log(friend.getGreeting()); // "Woof" console.log(Object.getPrototypeOf(friend) === dog); // true
这段代码定义了两个基对象:person
和dog
。两者多有getGreeting()
方法。friend
对象先继承person
对象,调用getGreeting()
方法输出"Hello";当原型被变动为dog
对象时,原先与person
对象的关联被解除,调用person.getGreeting()
方法时输出的内容就变为了"Woof"。
对象原型的真实值被存储在内部专用属性[[Prototype]]
中,调用Object.getPrototypeOf()
方法返回存储在其中的值,调用Object.setPrototypeOf()
方法改变其中的值。然而,这不是操做[[Prototype]]
值的惟一方法。
ES6
引入了super
引用的特性,使用它能够更便捷地访问对象原型。举个例子,若是你想重写对象实例方法,又须要调用与它同名的原型方法,先看看ES5
中的实现:
let person = { getGreeting() { return "Hello"; } }; let dog = { getGreeting() { return "Woof"; } }; 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
在这个示例中,friend
对象的getGreeting()
方法调用了同名的原型方法。Object.getPrototypeOf()
方法能够确保调用正确的原型,并向输出字符串叠加另外一个字符串;后面的.call(this)
能够确保正确设置原型方法中的this
值。
要准确记得如何使用Object.getPrototypeOf()
方法和.call(this)
方法来调用原型上的方法实在有些复杂,因此ES6
引入了super
关键字。super
引用至关于指向对象原型的指针:
let friend = { getGreeting() { // return Object.getPrototypeOf(this).getGreeting.call(this) + ", hi!"; return sper.getGreeting() + ", hi!"; } };