[译]es6基础篇--对象
申明:本系列文章均来自网络,内容按照自己的理解进行翻译,并不是照搬英文。如果想执行文章中的代码,请确保node版本大于0.11+;或者去在线网站
由于js中几乎所有变量都是object的缘故,大量es6的特性改动都专注于提高对象的实用性。此外,大量对象的使用在js编程中持续增加,意味着开发者多数工作都和对象相关,因此提高对象的实用性非常有必要。
es6从更简单的语法,到新的操作和维护对象方法来提高对象的实用性。
对象分类
es6详细介绍了几种新的方法来区别不同类别的对象。与浏览器之类的执行环境相反,js一直有一些不同的术语用于描述标准的对象。es6特地清楚的定义了每个对象的分类,而且重要的是,理解了这些描述对象的专业术语有助于理解整个js语言。这些对象的分类有:
-
普通对象:具有js中对象的所有内部行为
-
扩展对象:在某些场景下,内部行为和普通对象有所区别
-
标准对象:在es6中被定义,如:
Array
,Date
等。标准对象可以是普通对象和扩展对象 -
内置对象:存在于js脚本一开始执行时的执行环境, 所有的标准对象都是内置对象
这部分的基础知识贯穿整个es6系列,用于解释es6中定义的不同类别的对象
对象属性初始化简写
在es5中,对于初始化一个key和value字面量为一样的对象时,方式是这样的:
function createPerson(name, age) {
return {
name: name,
age: age
};
}
实际上,name和age两个变量是方法createPerson的两个局部变量,所以在es6中可以这样简写
function createPerson(name, age) {
return {
name,
age
};
}
es6中,你可以忽略掉属性名和局部变量一样的key,而直接使用局部变量的名。当js引擎发现一个对象只有key而没有value时,js引擎会在当前的上下文环境中查找一个名字和key相同的局部变量,如果找到。则把该局部变量的值作为当前key的值。
这样的简写方式对于创建对象更为便捷。
对象方法初始化简写
如上面的属性简写,对象中的方法也是可以简写的。如
var person = {
name: 'test',
sayName: function () {
console.log(this.name);
}
}
在es6中可以直接省略掉function关键字和:
,减少你在代码中输入无用字符的次数。
var person = {
name: 'test',
sayName() {
console.log(this.name);
}
}
这两种写法的最终效果是完全一致的,不会改变sayName中this的指向。
可计算的属性名
直接看代码
var lastName = 'last name';
var suffix = 'suffix';
var person = {
'first name': 'test',
[lastName]: 'last name',
['test' + suffix]: 'test test'
};
person['last-name']; // 'last name'
person['testsuffix']; // 'test test'
Object.assign(target, …sources)
Object.assign()
功能上类似mixin
和extend
, 它只会拷贝sources对象上属性为enumerable
和hasOwnProperty
的属性到target上。对于存取器方法的处理是:在sources上[[Get]],然后[[put]]到target上,即会把存取器的返回值放到target上,而并不是存取器本身。
var receiver = {};
var supplier = {
get name() {
return 'getter';
}
}
Object.assign(receiver, supplier, {
test: 'test'
});
console.log(receiver.name); // 'getter'
console.log(receiver.test); // 'test'
可重复的key
var person = {
name: 'first name',
name: 'last name'
}
console.log(person.name); // 'last name'
重复的key在es6中并不会报错(在es5的严格模式下是会报语法错误的),后面出现的key会重写之前的值。
动态改变对象的prototype
原型链是js中实现继承的基础,es6中让原型链的功能更加强大。
es5添加了Object.getPrototypeOf()
方法来得到任何一个给定对象的原型对象;es6添加一个配对的方法:Object.setPrototypeOf()
用于改变任何对象的原型链。
通常,一个对象的原型链在他被创建的时候就指明了,不管是通过构造函数方法还是Object.create()
方法。 在es6之前,没有一个标准的方法用于在一个对象被创建后动态改变他的原型链。
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
对象的原型对象是被存储在一个私有对象[[Prototype]]
上,Object.getPrototypeOf()
仅仅是返回存储在[[Prototype]]上的值,相反的。Object.setPrototypeOf()
只是改变了[[Prototype]]上存储的值。但是这里除了存取器的方法,并没有一个直接的途径去获取[[Prototype]]上存储的值。
es5标准完成后,几个js引擎已经实现了一个通用的属性__proto__
来直接操作[[Prototype]],实际上,__proto__
是
Object.setPrototypeOf()和
Object.getPrototypeOf()的早期版本,它不切实际的希望所有js引擎都移除掉这些属性,所以es6中规范化了
proto`的行为。
在es6中。Object.prototype.__proto__
属性完全可以实现和Object.getPrototypeOf()
和Object.setPrototypeOf()
一样的效果。
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
注意:
-
在对象字面量中
__proto__
仅能出现一次,出现多次会报错误 -
尽量用
__proto__
而不是['__proto__']
Super
引用
super
直接指向当前对象的原型链
let person = {
getGreeting() {
return "Hello";
}
};
// prototype is person
let friend = {
__proto__: person,
getGreeting() {
return super.getGreeting() + ", hi!";
}
};
// prototype is friend
let relative = {
__proto__: friend
};
console.log(person.getGreeting()); // "Hello"
console.log(friend.getGreeting()); // "Hello, hi!"
console.log(relative.getGreeting()); // "Hello, hi!"
对象的方法
在es6前,“方法”有一个非正式的定义:方法就是对象中属性为函数而不是纯数据的项。es6中正式定义了对象的方法,即如果属性有一个私有对象[[HomeObject]]
,那它就是对象的方法而不是属性,[[HomeObject]]
指向当前方法所属的对象。
let person = {
// method
getGreeting() {
return "Hello";
}
};
// not a method
function shareGreeting() {
return "Hi!";
}
如上的例子中定一个了只有一个方法getGreeting()
的person
对象,getGreeting()
方法的[[HomeObject]]
指向person
,而shareGreeting()
并没有设置[[HomeObject]]
,所以他就不是一个对象的方法. 在大多数场景下,这个特性看起来没什么用,但是结合super
,这就是一个非常了不起的功能,可以判断当前方法是不是一个对象的方法,就可以决定是不是可以使用super
的相同方法。
所有的super
引用都用[[HomeObject]]
来决定怎么做,首先在[[HomeObject]]
上调用Object.getPrototypeOf()
来得到当前实例的原型链,接下来,原型链去查找一个名字和当前执行函数一样的方法;最后,绑定执行函数的this环境,并执行该函数。如果一个方法没有[[HomeObject]]
或者跟期望的不同,那该过程就不会正常工作,例如:
ilet 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
上面的例子中,由于getGlobalGreeting()
方法并没有[[HomeObject]]
,所以在上面调用super
是会报错的。做如下改动:
// 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
这是由于[[HomeObject]]
只有在一个方法被定义的时候才会指定
相关阅读
- 上一篇:迁移博客到nginx
- 下一篇:[译]es6基础篇--Collections