ECMAScript6 Object

二、对象

A lot of ECMAScript 6 focused on improving the utility of objects. The focus makes sense given that nearly every value in JavaScript is represented by some type of object. Additionally, the number of objects used in an average JavaScript program continues to increase, meaning that developers are writing more objects all the time. With more objects comes the necessity to use them more effectively.

  ECMAScript6很多方面都注重于改变对象是功能。这种重视是有道理的,因为几乎javascript中的每一个值都是一种对象类型。此外,对象在javascript程序中的使用数量也在增长,意味着程序员一直在写很多的对象。对象越来越多,让对象变得更加有效也就越必要。

ECMAScript 6 improves objects in a number of ways, from simple syntax to new ways of manipulating and interacting with objects.

  ECMAScript通过一系列的方法来改变对象,从简单的语法到操作和与对象交互的新的方法。

2.1对象类别

The ECMAScript 6 specification introduced some new terminology to help distinguish between categories of objects. JavaScript has long been littered with a mix of terminology used to describe objects found in the standard as opposed to those that are added by execution environments such as the browser. ECMAScript 6 takes the time to clearly define each category of object, and it’s important to understand this terminology to have a good understanding of the language as a whole. The object categories are:

  ECMAScript6规范引入了一些新的术语来分辨categories和objects。长久以来,javascript对于描述标准中的对象和描述浏览器等执行环境中增加的对象的术语一直模糊不清。ECMAScript 6花时间清楚的定义了每一个类别的对象,理解这个术语对于理解整个语言都很重要。对象的类别有:

  • Ordinary objects are objects that have all of the default internal behaviors for objects in JavaScript.
  • 普通对象-是指那些拥有javascript所有内部默认行为的对象。
  • Exotic objects are objects whose internal behavior is different than the default in some way.
  • 异常对象-是指那些内部行为与默认行为在某些方面不相同的对象
  • Standard objects are objects defined by ECMAScript 6, such as Array, Date, etc. Standard objects may be ordinary or exotic.
  • 标准对象 - ECMAScript中定义的对象,例如Array、Date、等等。标准对象可能是普通对象也可能是异常对象。
  • Built-in objects are objects that are present in a JavaScript execution environment when a script begins to execute. All standard objects are built-in objects.
  • 内置对象-当脚本开始执行,对象的父亲在javascript执行环境中。所有的标准对象都是内置对象。

These terms are used throughout the book to explain the various objects defined by ECMAScript 6.

  本书中都用这些术语来解释ECMAScript中定义的对象。

2.2、对象字面量扩展

One of the most popular patterns in JavaScript is the object literal. It’s the syntax upon which JSON is built and can be seen in nearly every JavaScript file on the Internet. The reason for the popularity is clear: a succinct syntax for creating objects that otherwise would take several lines of code to accomplish. ECMAScript 6 recognized the popularity of the object literal and extends the syntax in several ways to make object literals more powerful and even more succinct.

  javascript中最流行的一种形式就是对象字面量。它通过JSON的语法撞见,在互联网上的几乎每一个js文件中都能看到。这种形式流行的原因:创建对象的语法形式简单,其他的形式可能需要更多的行来完成。ECMAScript6意识到了对象字面量这种形式的流行,所以在几个方面扩展了它的语法使得对象字面量更加强大更加简洁。

2.3、属性初始化速写

In ECMAScript 5 and earlier, object literals were simply collections of name-value pairs. That meant there could be some duplication when property values are being initialized. For example:

  在ECMAScript5 和 之前的版本中,对象字面量只是键-值对的集合。这意味着,在属性初始化的时候可能会存在一些重复工作。例如:

function createPerson(name, age) {
    return {
        name: name,
        age: age
    };
}

 The createPerson() function creates an object whose property names are the same as the function parameter names. The result is what appears to be duplication of name and age even though each represents a different aspect of the process.

  createPerson函数创建了一个对象,这个对象的属性名字和函数的参数名字相同。结果是name和value出现了两次,尽管它们代表不同方面的过程。

In ECMAScript 6, you can eliminate the duplication that exists around property names and local variables by using the property initializer shorthand. When the property name is going to be the same as the local variable name, you can simply include the name without a colon and value. For example, createPerson() can be rewritten as follows:

  在ECMAScript6中,通过使用属性初始化的速写功能可以消除属性命和变量名重复的情况。当属性名字和变量名字相同的时候,你可以简单的使用名字不需要冒号和值。例如,createPerson可以重写成以下形式:

function createPerson(name, age) {
    return {
        name,
        age
    };
}

 When a property in an object literal only has a name and no value, the JavaScript engine looks into the surrounding scope for a variable of the same name. If found, that value is assigned to the same name on the object literal. So in this example, the object literal property name is assigned the value of the local variable name.

  当对象字面量的属性只有名字没有值的时候,javascript引擎在包裹的作用域寻找相同名字的变量。如果找到了,就把变量的值赋给相同名字的对象属性。所以在本例中,对象属性name的值是由变量name赋给的。

The purpose of this extension is to make object literal initialization even more succinct than it already was. Assigning a property with the same name as a local variable is a very common pattern in JavaScript and so this extension is a welcome addition.

  这种扩展的目的是让对象字面量的初始化比现在更加简洁。在javascript中,名字相同的变量给同名字的属性赋值很普遍,所以这种扩展非常受欢迎。

2.4、方法初始化速写

  ECMAScript 6 also improves syntax for assigning methods to object literals. In ECMAScript 5 and earlier, you must specify a name and then the full function definition to add a method to an object. For example:

  ES6也改进了对象字面量方法的赋值语法。在ES5和之前的版本中,必须指定为对象的方法指定名字和完成的函数定义。例如:

var person = {
    name: "Nicholas",
    sayName: function() {
        console.log(this.name);
    }
};

 In ECMAScript 6, the syntax is made more succinct by eliminating the colon and the function keyword. You can then rewrite the previous example as:

   在ES6中,通过删除冒号和function关键字,语法更加的简洁。前一个例子可以重写如下;

var person = {
    name: "Nicholas",
    sayName() {
        console.log(this.name);
    }
};

 This shorthand syntax creates a method on the person object just as the previous example did. There is no difference aside from saving you some keystrokes, so sayName() is assigned an anonymous function expression and has all of the same characteristics as the function defined in the previous example.

  使用速写语法给person对象创建了一个方法。除了节省了敲击键盘的次数没有任何区别,所以sayName被赋予了一个匿名函数表达式,和之前例子中定义的函数有着完全一样的特性。

The name property of a method created using this shorthand is the name used before the parentheses. In the previous example, the name property for person.sayName() is "sayName".

  注意:使用速写形式创建的方法的name属性是圆括号之前的那个名字。在前一个例子中,person.sayName的name属性是“sayName”。

2.5、可计算的属性名字

  JavaScript objects have long had computed property names through the use of square brackets instead of dot notation. The square brackets allow you to specify property names using variables and string literals that may contain characters that would be a syntax error if used in an identifier. For example:

  通过使用方括号代替点符号,javascript对象可以使用计算出的属性的名字。方括号允许你使用变量指定一个属性的名字,或者字符串中包含了一些特殊的会导致错误的标识符来指定属性的名字。例如:

var person = {},
    lastName = "last name";

person["first name"] = "Nicholas";
person[lastName] = "Zakas";

console.log(person["first name"]);      // "Nicholas"
console.log(person[lastName]);          // "Zakas"

 Both of the property names in this example have a space, making it impossible to reference those names using dot notation. However, bracket notation allows any string value to be used as a property name.

  本例中的两个属性名字中间都有一个空格,无法使用点符号来引用这些名字。然而,方括号允许任何形式的字符串作为属性名字使用。

In ECMAScript 5, you could use string literals as property names in object literals, such as:

  在ES5中,在对象字面量中,你可以使用字符串作为属性名字,例如:

var person = {
    "first name": "Nicholas"
};

console.log(person["first name"]);      // "Nicholas"

 If you could provide the string literal inside of the object literal property definition then you were all set. If, however, the property name was contained in a variable or had to be calculated, then there was no way to define that property using an object literal.

  如果你能为对象字面量中的每一个属性提供一个字符串定义,那么你都设置。然而,如果属性的名字包含变量或者需要计算,那么没有办法通过对象字面量来定义这个属性。

ECMAScript 6 adds computed property names to object literal syntax by using the same square bracket notation that has been used to reference computed property names in object instances. For example:

  ES6中为对象字面量增加了计算的属性名字语法-通过方括号,与对象实例中使用方括号来引用计算的属性名字的方式一样。例如:

var lastName = "last name";

var person = {
    "first name": "Nicholas",
    [lastName]: "Zakas"
};

console.log(person["first name"]);      // "Nicholas"
console.log(person[lastName]);          // "Zakas"

 The square brackets inside of the object literal indicate that the property name is computed, so its contents are evaluated as a string. That means you can also include expressions such as:

  对象字面量中的方括号里面的值表示属性命是通过计算得来的,所以它得内容是字符串。这意味着你也可以包含表达式,例如:

var suffix = " name";

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

console.log(person["first name"]);      // "Nicholas"
console.log(person["last name"]);       // "Zakas"

 Anything you would put inside of square brackets while using bracket notation on object instances will also work for computed property names inside of object literals.

  任何能放入对象实例中方括号得内容你都可以放入对象字面量的方括号里面。  

2.6、Object.assign()

  One of the most popular patterns for object composition is mixins, in which one object receives properties and methods from another object. Many JavaScript libraries have a mixin method similar to this:

  对象最受欢迎的模式是多态,对象从另一个对象中接受属性和方法。很多javascript库都有类似的多态方法:

function mixin(receiver, supplier) {
    Object.keys(supplier).forEach(function(key) {
        receiver[key] = supplier[key];
    });

    return receiver;
}

 The mixin() function iterates over the own properties of supplier and copies them onto receiver. This allows the receiver to gain new behaviors without inheritance. For example:

  mixin函数遍历supplier中的每一个属性,复制这些属性给receiver。这样receiver就不需要继承就增加了新的行为。例如:

function EventTarget() { /*...*/ }
EventTarget.prototype = {
    constructor: EventTarget,
    emit: function() { /*...*/ },
    on: function() { /*...*/ }
};

var myObject = {};
mixin(myObject, EventTarget.prototype);

myObject.emit("somethingChanged");

 In this example, myObject receives behavior from EventTarget.prototype. This gives myObject the ability to publish events and let others subscribe to them using emit() and on(), respectively.

  在这个例子中,myObject从EventTarget.prototype中获取行为。这样myObject发布events的能力,其他的用户可以分别使用emit和on方法。

This pattern became popular enough that ECMAScript 6 added Object.assign(), which behaves the same way. The difference in name is to reflect the actual operation that occurs. Since the mixin() method uses the assignment operator (=), it cannot copy accessor properties to the receiver as accessor properties. The name Object.assign() was chosen to reflect this distinction.

  ES6增加了Object.assign()方法和这种形式的行为一样。名字的差异反应了实际的操作。mixin函数使用了赋值操作符(=),这不能为receiver为属性复制一个访问器。Object.assign()的名字反应了这种区别。

Similar methods in various libraries may have other names. Some popular alternate names for the same basic functionality are extend() and mix(). There was also, briefly, an Object.mixin() method in ECMAScript 6 in addition to Object.assign(). The primary difference was that Object.mixin() also copied over accessor properties, but the method was removed due to concerns over the use of super (discussed later in this chapter).

  注意:同样的方法在不同的库中可能会使用不同的名字。类似功能的函数名字有mix和extend。同样ES6除了Object.assign(),也增加了Object.mixin方法。主要的区别在于mixin同样是复制访问器,但是这个因为考虑到super的使用而移除了(在本章稍后讨论)

You can use Object.assign() anywhere the mixin() function would have been used:

  你可以在任何能使用minin函数的地方使用Object.assign():

function EventTarget() { /*...*/ }
EventTarget.prototype = {
    constructor: EventTarget,
    emit: function() { /*...*/ },
    on: function() { /*...*/ }
}

var myObject = {}
Object.assign(myObject, EventTarget.prototype);

myObject.emit("somethingChanged");

 The Object.assign() method accepts any number of suppliers, and the receiver receives the properties in the order in which the suppliers are specified. That means the second supplier might overwrite a value from the first supplier on the receiver. For example:

   Object.assign()方法接收任意数量的supplier,receiver按照顺序接收suppliers指定的属性。意味着第二个supplier可能会重写第一个supplier提供的值,例如:

var receiver = {};

Object.assign(receiver, {
        type: "js",
        name: "file.js"
    }, {
        type: "css"
    }
);

console.log(receiver.type);     // "css"
console.log(receiver.name);     // "file.js"

 The value of receiver.type is "css" because the second supplier overwrote the value of the first.

  receiver.type的值是css,因为第二个supplie重写了第一个。

The Object.assign() method isn’t a big addition to ECMAScript 6, but it does formalize a common function that is found in many JavaScript libraries.

  Object.assign()方法在ES6中并不是一个重大的改进,但是它规范了大多数js库中的普遍使用的函数。


Working with Accessor Properties

访问器属性

Keep in mind that you cannot create accessor properties on the receiver by using a supplier with accessor properties. Since Object.assign() uses the assignment operator, an accessor property on a supplier will become a data property on the receiver. For example:

牢记,你不能通过supplier的访问器特性来为receiver创造访问器特性。因为Object.assign()使用赋值运算符,supplier中的访问器特性将会成为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

 

In this code, the supplier has an accessor property called name. After using Object.assign(), receiver.name exists as a data property with the value of "file.js". That’s because supplier.name returned “file.js” at the time Object.assign() was called.

  在本例中,supplier有一个访问器属性叫做name,通过使用assign方法,receiver的name以数据属性的形式存在,它的值是“file.js”。因为当assign调用的时候supplier的name属性返回“file.js”。


2.7、重复的对象字面量属性

  ECMAScript 5 strict mode introduced a check for duplicate object literal properties that would throw an error if a duplicate was found. For example:

  在ES5的strict模式中,会检测对象字面量属性的重复性,如果存在重复就会抛出错误。例如:

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

  When running in ECMAScript 5 strict mode, this example results in a syntax error on the second name property.

  当在ES5严格模式下执行这段代码的时候,会在第二个name属性的地方抛出错误。

In ECMAScript 6, the duplicate property check has been removed. Both strict and nonstrict mode code no longer check for duplicate properties and instead take the last property of the given name as the actual value.

  在ES6中,移除了重复属性的检查。严格和非严格模式下都不在检查重复的属性,属性的实际值取最后一个给定的值。

ar person = {
    name: "Nicholas",
    name: "Greg"        // not an error in ES6
};

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

 In this example, the value of person.name is "Greg" because that was the last value assigned to the property.

  在本例子中,person.name的值是“Greg”,因为那是最后一个赋给属性的值。

2.8、改变原型

  Prototypes are the foundation of inheritance in JavaScript, and so ECMAScript 6 continues to make prototypes more powerful. ECMAScript 5 added the Object.getPrototypeOf() method for retrieving the prototype of any given object. ECMAScript 6 adds the reverse operation, Object.setPrototypeOf(), which allows you to change the prototype of any given object.

  原型是js继承的基础,ES6中延续原型的强大功能。ES5增加了Object.getPrototypeOf()方法来检索指定对象的原型。ES6增加了相反的操作,Object.setPrototypeOf(),允许你改变指定对象的原型。

Normally, the prototype of an object is specified at the time of its creation, either by using a constructor or via Object.create(). Prior to ECMAScript 6, there was no standard way to change an object’s prototype after it had already been created. In a way, Object.setPrototypeOf() changes one of the biggest assumptions about objects in JavaScript to this point, which is that an object’s prototype remains unchanged after creation.

  通常,对象的原型在它创建的时候就指定了,无论是通过构造函数还是Object.create()。ES6之前,对象创建之后没有一个标准的方法来改变对象的原型。在某种程度上, 在这一点Object.setPrototypeOf()改变了关于js对象的最大设想-对象的原型在创建之后保持不变。

The Object.setPrototypeOf() method accepts two arguments, the object whose prototype should be changed and the object that should become the first argument’s prototype. For example:

  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

 This code defines two base objects: person and dog. Both objects have a method getGreeting() that returns a string. The object friend starts out inheriting from person, meaning that getGreeting() will output "Hello". When the prototype is changed to be dog instead, person.getGreeting() outputs "Woof" because the original relationship to person is broken.

  这段代码定义了两个基本的对象:person和dog。这两个对象都有一个返回一个字符串的getGreeting方法。开始对象friend从person继承,getGreeting会输出Hello。当原型改变为dog之后,person.getGreeting() 输出 "Woof",因为与初始person的连接断了。

The actual value of an object’s prototype is stored in an internal-only property called [[Prototype]]. The Object.getPrototypeOf() method returns the value stored in [[Prototype]] and Object.setPrototypeOf() changes the value stored in [[Prototype]]. However, these aren’t the only ways to work with the value of [[Prototype]].

  对象原型的真实的值存储在内部属性[[Prototype]]中。Object.getPrototypeOf()方法返回存储在[[Prototype]]的值,Object.setPrototypeOf()改变存储在[[Prototype]]的对象。然而操作[[Prototype]]的方式不仅仅只有这两种方法。

Even before ECMAScript 5 was finished, several JavaScript engines already implemented a custom property called __proto__ that could be used to both get and set the prototype of an object. Effectively, __proto__ was an early precursor to both Object.getPrototypeOf() and Object.setPrototypeOf(). It was unrealistic to expect all of the JavaScript engines to remove this property, so ECMAScript 6 formalized the behavior of __proto__.

  早在ES5完成之前,一些js引擎已经实现了一个自定义属性叫做__proto__,可以用来获取和改变对象的原型,实际上,__proto__是Object.getPrototype()和Object.setPrototype()的先驱。希望所有的浏览器引擎都删除这个属性是不切实际的。所以,ES6规范了__proto__的行为。

In ECMAScript 6 engines, Object.prototype.__proto__ is defined as an accessor property whose get method calls Object.getPrototypeOf() and whose set method calls Object.setPrototypeOf(). This means that there is no real difference between using __proto__ and the other methods except that __proto__ allows you to set the prototype of an object literal directly. For example:

  在ES6中引擎中,Object.prototype.__proto__被定义成一个访问器属性,get方法叫做Object.getPrototypeOf(),set方法叫做Object.setPrototypeOf()。这意味着使用__proto__和其他的方法没有太大的区别,除了__proto__允许你直接为对象字面量直接设置原型。例如:

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

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

// prototype is person
let friend = {
    __proto__: person
};
console.log(friend.getGreeting());                      // "Hello"
console.log(Object.getPrototypeOf(friend) === person);  // true
console.log(friend.__proto__ === person);               // true

// set prototype to dog
friend.__proto__ = dog;
console.log(friend.getGreeting());                      // "Woof"
console.log(friend.__proto__ === dog);                  // true
console.log(Object.getPrototypeOf(friend) === dog);     // true

This example is functionally equivalent to the previous. The call to Object.create() has been replaced with an object literal that assigns a value to __proto__. The only real difference between creating an object using Object.create() or an object literal with __proto__ is that the former requires you to specify full property descriptors for any additional object properties while the latter is just a standard object literal. 

  这个例子和之前的例子在功能上相同。调用Object.create方法被通过对象字面量赋值__proto__的方式取代了。使用Object.create()和__proto__创建对象的唯一的区别在于前者需要你指定任何附加的对象属性指定全部的属性描述符,而后者只是一个标准的对象字面量。

The __proto__ property is special in a number of ways:

  在一下方面,__proto__属性比较特殊。

  1. You can only specify it once in an object literal. If you specify two __proto__ properties, then an error is thrown. This is the only object literal property that has this restriction.
  2. 你只能在对象字面量中指定一次。如果你指定了两个__proto__属性,会抛出错误。这是唯一的一个对象字面量属性有这种限制。
  3. The computed form ["__proto__"] acts like a regular property and does not set or return the current object’s prototype. All rules related to object literal properties apply in this form, as opposed to the non-computed form, for which there are exceptions.
  4. 类似于普通属性使用计算的形式["__proto__"]不会shezhi或返回当前的对象原型。对象字面量的所有相关规则都适用于这种形式,而不是non-computed的形式,只有这个是例外。

It’s best to be careful when using __proto__ to make sure you don’t get caught by these differences.

  当使用__proto__的时候最好小心,确保你不会被这些不同所影响。


 

2.9、super引用

 As previously mentioned, prototypes are very important for JavaScript and a lot of work went into making them easier to use in ECMAScript 6. Among the improvements is the introduction of super references to more easily access functionality on an object’s prototype. For example, if you want to override a method on an object instance such that it also calls the prototype method of the same name, you would need to do the following in ECMAScript 5:

  之前提到过,原型在js中非常重要,ES6做了许多工作使得原型的使用更加简单。在这些改进工作中,super引用的引进让使用对象原型的功能更加容易,如果你想在对象实例上重写一个与原型同名的方法,在ES5中你需要这样做:

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

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

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

console.log(friend.getGreeting());                      // "Hello, hi!"
console.log(Object.getPrototypeOf(friend) === person);  // true
console.log(friend.__proto__ === person);               // true

// set prototype to dog
friend.__proto__ = dog;
console.log(friend.getGreeting());                      // "Woof, hi!"
console.log(friend.__proto__ === dog);                  // true
console.log(Object.getPrototypeOf(friend) === dog);     // true

 

In this example, getGreeting() on friend calls the prototype method of the same name. The Object.getPrototypeOf() method is used to ensure the method is always getting the accurate prototype and then an additional string is appended. The additional .call(this) ensures that the this value inside the prototype method is set correctly.

   在这个例子中,friend函数的getGreeting方法的调用了原型同样的名字的方法。Object.getPrototypeOf()方法确保总是从正确的原型中获得方法然后添加一个字符串。附加的call(this)确保原型中的this值与当前的this值相同。

 Needing to remember to use Object.getPrototypeOf() and .call(this) to call a method on the prototype is a bit involved, so ECMAScript 6 introduced super.

   需要记住使用Object.getPrototypeOf()和.call(this)来获得原型中的方法有一点复杂,所以ES6引入了super。

At it’s simplest, super acts as a pointer to the current object’s prototype, effectively acting like Object.getPrototypeOf(this). So you can simplify the getGreeting() method by rewriting it as:

  最简单的,super实际上是当前原型的一个指针,有效的模拟Object.getPrototypeOf(this)。所以上面的例子可以重写如下:

let friend = {
    __proto__: person,
    getGreeting() {
        // same as Object.getPrototypeOf(this).getGreeting.call(this)
        // or this.__proto__.getGreeting.call(this)
        return super.getGreeting() + ", hi!";
    }
};

 The call to super.getGreeting() is the same as Object.getPrototypeOf(this).getGreeting.call(this) or this.__proto__.getGreeting.call(this). Similarly, you can call any method on an object’s prototype by using a super reference.

  调用super.getGreeting和调用Object.getPrototypeOf(this).getGreeting.call(this)或者this.__proto__.getGreeting.call(this)的功能一样。同样的,你可以使用super调用对象原型上的任何方法。

If you’re calling a prototype method with the exact same name, then you can also call super as a function, for example:

  如果你正在调用原型上的同名方法,你可以像调用函数那样使用super,例如:

let friend = {
    __proto__: person,
    getGreeting() {
        // same as Object.getPrototypeOf(this).getGreeting.call(this)
        // or this.__proto__.getGreeting.call(this)
        // or super.getGreeting()
        return super() + ", hi!";
    }
};

 Calling super in this manner tells the JavaScript engine that you want to use the prototype method with the same name as the current method. So super() actually does a lookup using the containing function’s name property (discussed in Chapter 2) to find the correct method.

  以这种方式调用super是告诉js引擎你希望调用与当前方法同名的原型方法。所以super()实际上在包裹的函数中寻找函数的name属性来找到正确的方法。


super references can only be used inside of functions and cannot be used in the global scope. Attempting to use super in the global scope results in a syntax error.

注意:super引用只能在函数内部使用,不能在全局作用域中使用。在全局作用域中使用会导致错误。


 

2.10 方法

Prior to ECMAScript 6, there was no formal definition of a “method” - methods were just object properties that contained functions instead of data. ECMAScript 6 formally defines a method as a function that has an internal [[HomeObject]] property containing the object to which the method belongs. Consider the following:

  ES6版本之前,关于“方法”没有正式的定义-方法是对象的属性,只不过包含的是函数而不是数据。ES6正式的定义了方法-一个函数拥有内置属性[[HomeObject]]包含了这个方法所从属于的对象。思考下面的例子:

let person = {

    // method
    getGreeting() {
        return "Hello";
    }
};

// not a method
function shareGreeting() {
    return "Hi!";
}

 This example defines person with a single method called getGreeting(). The [[HomeObject]] for getGreeting() is person by virtue of assigning the function directly to an object. The shareGreeting() function, on the other hand, has no [[HomeObject]] specified because it wasn’t assigned to an object when it was created. In most cases this difference isn’t important, but it becomes very important when using super.

  这个例子定义了person只有一个getGreeting方法。getGreeting的[[HomeObjexct]] 属性是person直接分配给对象的函数。另一方面,shareGreeting函数没有[[HomeObject]]属性,因为它创建的时候没有分配给对象,在大多数情况下,这些区别不是太重要,当使用super的时候就变得很重要了。

Any reference to super uses the [[HomeObject]] to determine what to do. The first step is to call Object.getPrototypeOf() on the [[HomeObject]] to retrieve a reference to the prototype. Then, the prototype is searched for a function with the same name as the executing function. Last, the this-binding is set and the method is called. If a function has no [[HomeObject]], or has a different one than expected, then this process won’t work. For example:

  任何对super的引用都使用了[[HomeObject]]来决定做什么。第一步,在[[HomeObject]]上调用Object.getPrototypeOf()来找到原型的引用。然后,原型搜索与当前执行函数同名的方法。最后this绑定方法被调用。如果一个函数没有[[HomeObject]],或者与期望的值不同,那么上述过程将不再执行,例如:

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

// prototype is person
let friend = {
    __proto__: person,
    getGreeting() {
        return super() + ", hi!";
    }
};

function getGlobalGreeting() {
    return super.getGreeting() + ", yo!";
}

console.log(friend.getGreeting());  // "Hello, hi!"

getGlobalGreeting();               // throws error

Calling friend.getGreeting() returns a string while calling getGlobalGreeting() throws an error for improper use of super. Since the getGlobalGreeting() function has no [[HomeObject]], it’s not possible to perform a lookup. Interestingly, the situation doesn’t change if getGlobalGreeting() is later assigned as a method on friend:

  调用 friend.getGreeting()返回一个zifuchuan而调用getGlobalGreeting则是抛出了一个错误因为对super不正确的使用。因为getGlobalGreeting函数美哦与[[HomeObject]],不能执行查找。如果getGlobalGreeting在之后赋给friend作为一个方法,这种情况也不会改变。

// prototype is person
let friend = {
    __proto__: person,
    getGreeting() {
        return super() + ", hi!";
    }
};

function getGlobalGreeting() {
    return super.getGreeting() + ", yo!";
}

console.log(friend.getGreeting());  // "Hello, hi!"

// assign getGreeting to the global function
friend.getGreeting = getGlobalGreeting;

friend.getGreeting();               // throws error

 Here the global getGlobalGreeting() function is used to overwrite the previously-defined getGreeting() method on friend. Calling friend.getGreeting() at that point results in an error as well. The value of [[HomeObject]] is only set when the function is first created, so even assigning onto an object doesn’t fix the problem.

  这个全局函数getGlobalGreeting重写了之前在friend上定义的getGreeting方法。调用friend.getGreeting()方法也会抛出异常。因为[[HomeObject]]只有在函数第一次创建的时候才会设置,所以尽管赋给了对象当时并没有解决这个问题。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

  

posted @ 2015-03-16 10:44  丁家小花花  阅读(258)  评论(0编辑  收藏  举报