对象类别
- 普通( Ordinary )对象:具有JavaScript对象所有的默认内部行为;
- 特异( Exotic )对象:具有某些与默认行为不符的内部行为;
- 标准( Standard )对象:ES6规范中定义的对象,例如Array、Date等。标准对象既可以是普通对象,也可以使特异对象。
- 内建对象:脚本开始执行时存在于JavaScript执行环境中的对象,所有标准对象都是内建对象
对象字面量语法扩展
- 属性初始值的简写
在我们传统的JS中,对象字面量只是简单的键值对集合,这意味着初始化属性时会有一些重复,举个例子:
function createPerson( name, age ){
return {
name : name, //name写了两遍,一遍是属性名,一遍是变量
age : age //age写了两遍,一遍是属性名,一遍是变量
}
}
在ES6中我们对上面的属性初始化进行了简化,当一个对象的属性与本地变量同名时,不必再写冒号和值,简单地只写属性名即可:
function createPerson( name, age ){
return {
name ,
age
}
}
/*
* 当对象字面量里只有一个属性的名称时,JS引擎会在可以访问作用域中查找其同名变量;
* 如果找到,则该变量的值被赋给对象字面量里的同名属性。
* 在本例中,对象字面量属性name被赋予了局部变量name的值。
*/
- 对象方法的简写语法
在ES6之前,我们为对象添加方法,必须通过制定名称并完整定义函数:
let person = {
name : '欧阳不乖',
sayName : function(){
console.log(this.name)
}
}
而在ES6中,语法更简洁,消除了冒号和function关键字,让我们来重写上边的例子:
let person = {
name : '欧阳不乖',
sayName(){
console.log(this.name)
}
}
这种简洁写法与传统的定义对象方法所具有的特性全部一致,二者唯一的区别是,简写方法可以使用super关键字(稍后讨论)
- 可计算属性名
在早期函数中,我们如果想要通过计算得到属性名,只能用方括号代替点的方式:
// 我们想要的效果:
let name = 'unknown';
let person = {};
person[ name ] = '欧阳不乖';
console.log(person); // {unknown: "欧阳不乖"}
/*
* 这里我们要是换一种写法,就会是不一样的结果了
* let name = 'unknown';
* let person = {
* name : '欧阳不乖'
* };
* console.log(person); // {name: "欧阳不乖"}
* 当属性被包含在一个变量中的时候,这种赋值方式和我们设想的就不一样了
*/
在ES6中,可在对象字面中使用可计算属性名称,其语法与引用对象实例的可计算属性名称相同,也是使用方括号:
let name = 'unknown';
let person = {
[ name ] : '欧阳不乖'
};
console.log(person); // {unknown: "欧阳不乖"}
扩展:
let suffix = 'name';
let person = {
['full-'+suffix ] : '欧阳不乖',
[`chinese-${suffix}`] : '欧阳不乖'
};
console.log(person); //{full-name: "欧阳不乖", chinese-name: "欧阳不乖"}
新增API方法
- Object.is()方法
Object.is()方法来弥补全等运算符的不准确运算。这个方法接受两个参数,如果这两个参数类型相同且具有相同的值,则返回true。
其发部分运行结果与“===”运算符相同,唯一的区别在于+0和-0被识别为不相等且NaN与NaN等价。
console.log( '欧阳不乖'==='欧阳不乖' ); // true
console.log( Object.is('欧阳不乖','欧阳不乖') ); // true
console.log( +0 === -0 ); // true
console.log( Object.is(+0, -0) ); // false
console.log( NaN === NaN ); // false
console.log( Object.is(NaN,NaN) ); // true
/* 具体使用哪一种比较方法,主要取决于业务需要 */
- Object.assign()方法
接受任意数量的源对象,并按指定的顺序将属性复制到接收对象中。所以如果多个源对象局哟楼同名属性,则排位靠后的源对象会覆盖排位靠前的。
let receiver = {} ;
Object.assign(receiver,
{
type : 'js' ,
name : 'file.js'
},{
type : 'css'
}
);
console.log( receiver ); //{type: "css", name: "file.js"}
console.log( receiver.type ); //'css'
console.log( receiver.name ); //'file.js'
重复的对象字面量属性
在ES5严格模式下,对象字面量属性名重复时会抛出错误
而在ES6中,字面量属性名称如果重复的话,只会选取最后一个取值:
let person = {
name : 'unKnown',
name : '欧阳不乖'
}
console.log(person.name); // '欧阳不乖'
自由属性枚举顺序
在ES5中未定义对象属性的枚举顺序,由Javascript引擎厂商自行决定。
在ES6中严格规定了对象的自由属性被枚举时的返回顺序。
自有属性枚举顺序的基本规则是:
- 所有数字键按升序排序
- 所有字符串键按照它们被加入对象的顺序排序
- 所有symbol键按照它们被加入对象的顺序排序
let obj = {
a:1,
0:1,
c:1,
2:1,
b:1,
1:1
}
obj.d = 1;
console.log(Object.getOwnPropertyNames(obj).join('')); //012acbd
对于数值键,尽管在对象自绵中的顺序是随意的,但是在枚举时会被重新组合和排序
对于for-in、Object.keys和JSON.stringify()方法暂时仍按照各引擎厂商的自行实现顺序枚举
增强对象原型
- 改变对象的原型
正常情况下,对象原型是在对象被创建的时候指定的,对象原型在实例化之后保持不变。
在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
Object.setPrototypeOf(friend,dog);
console.log(friend.getGreeting()); // 'Woof'
console.log(Object.getPrototypeOf(friend)===dog); // true
对象原型的真实值被存储在内部专用属性[[Prototype]]中,调用Object.getPrototypeOf()方法返回存储在其中的值,调用Object.setPrototypeOf()方法改变其中的值
- 简化原型访问的Super引用
ES6中引入了Super引用的特性,使用它可以更便捷地访问原型对象。
let person = {
getGreeting(){
return 'Hello';
}
};
let dog = {
getGreeting(){
return 'Woof';
}
}
let friend = {
getGreeting(){
return super.getGreeting() + ',nice to meet you. '
// 等价于 Object.getPrototypeOf(this).getGreeting().call(this)
}
}
简单来说,Super引用相当于指向对象原型的指针,实际上也就是Object.getPrototypeOf(this)的值。
Super引用不是动态变化的,它总是指向正确的对象。
Super方法必须在简写方法的对象中使用,其他地方引用会导致语法错误。
正式的方法定义
在上边我们用Super的时候指明要求只能在简写方法的对象中使用,下面我们来解释一下原因。
在ES6中正式将方法定义为一个函数,它会有一个内部的[[HomeObject]]属性来容纳这个而方法从属的对象:
let person = {
// 是方法
getGreeting(){
return 'Hello';
}
}
// 不是方法
function shareGreeting(){
return 'Hi !'
}
这个示例中定义了person对象,它有一个getGreeting()方法,由于直接把函数赋值给了person对象,因而getGreeting()方法的[[HomeObject]]属性值为person。而创建shareGreeting()函数时,由于未将其赋值给一个对象,因为该方法没有明确定义[[HomeObject]]属性。在大多数情况下这点小差别无关紧要,但是当使用Super引用时就变得非常重要了。
Super的所有引用都通过[[HomeObject]]属性来确定后续的运行过程,第一步是在[[HomeObject]]属性上调用Object.getPrototypeOf()方法来检索原型的引用:然后搜寻原型找到同名函数;最后,设置this绑定并且调用相应的方法。
注意:这里定义的是方法,而非函数,二者都是函数