在JavaScript中,几乎每一个值都是某种特定类型的对象,所以ES6加强了对象的功能性。
对象类别
·普通对象:具有JavaScript对象所有默认内部行为。
·特异对象:具有某些与默认行为不符的内部行为。
·标准对象:ECMAScript6规范中定义的对象,例如:Array,Date等。标准对象既可以是普通对象,也可以是特异对象。
·内建对象:脚本开始执行时存在于JavaScript执行环境中的对象,所有标准对象都是内建对象。
对象字面量语法扩展
属性初始值的简写
在ES5中,对象字面量只是简单的键值对集合,这意味着初始化属性值时会有一些重复。
function createPerson(name,age){
return {
name:name,
age:age
};
}
这段代码中这个函数属性名称与函数的参数相同,在ES6中,通过使用属性初始化的简写语法,就可以消除这种属性名称与局部变量之间的重复书写。
//这是简写
function createPerson(name,age){
return {
name,
age
};
}
当对象字面量里只有一个属性的名称时,JavaScript引擎会在可访问作用域中查找其同名变量;如果找到,就会把变量的值赋值给对象字面量的同名属性。这样做的好处是:有助于消除命名错误。
对象方法的简写语法
ES6也改进了为对象字面量定义方法的语法。
// ES6之前
var persoon={
name:"cc",
sayName:function(){
console.log(this.name);
}
};
//ES6中消除了冒号和function关键字
var person={
name:"cc",
sayName(){
console.log(this.name);
}
}
简写方法和之前的定义方法唯一不同的是简写方法可以使用super关键字(稍后讨论)。
可计算属性名
在ES6之前,如果属性名称被包含在变量里或者通过计算获得该变量的值,那么ES5并不能为一个对象字面量定义该属性的。
而在ES6中,可在对象字面量中使用可计算属性名称,其语法与引用对象实例的可计算属性名称相同,也是使用方括号。
//例子一
let lastName="last name";
let person={
"first name":"Nicholas",
[lastName]:"Zakes"
};
console.log(person["first name"]);//"Nicholas"
console.log(person[lastName]);//"Zakes"
//例子二
var suffix="name";
var person={
["first"+suffix]:"Nicholas",
["last"+suffix]:"Zakes"
};
console.log(person["firstname"]);//"Nicholas"
console.log(person["lastname"]);//"Zakes"
新增方法
ES6为了让某些任务也更容易完成,在全局对象Object对象中引入了新的方法。
Object.is()方法
ES5前,开发者习惯用===确定比较值,但是+0和-0在JavaScript中是是不同实体,然后+0===-0,同样,NaN===NaN会返回false,而ES6中引入Object.is()弥补全等运算符的不准确性。
console.log(+0===-0);//true
console.log(NaN===NaN);//false
console.log(Object.is(+0,-0));//false
console.log(Object.is(NaN,NaN));//true
Object.assign()方法
混合(Mixin)是JavaScript中实现对象组合最流行的模式,它可以实现一个对象接收来自另一个对象的属性和方法。
function mixin(receiver,supplier){
Object.keys(supplier).forEach(function(key){//遍历自身属性,并添加到新对象中
receiver[key]=supplier[key];
});
return receiver;
}
这样一来利用这个函数不通过继承就可以获得新属性。
function EventTarget(){/*...*/}
EventTarget.prototype={
constructor:EventTarget,
emit:function(){return "cc";},
on:function(){/*...*/}
};
var myObject={};
mixin(myObject,EventTarget.prototype);
myObject.emit("somethingChanged");//"cc"
这种混合模式非常流行,所以在ES6中添加了Object.assign()方法实现了相同的功能,这个方法接收对象和任意数量的源对象,最终返回对象。值得注意的是不能复制访问器属性。
任何使用mixin()的方法都可以直接使用这个方法。
function EventTarget(){/*...*/}
EventTarget.prototype={
constructor:EventTarget,
emit:function(){return "cc";},
on:function(){/*...*/}
};
var myObject={};
Object.assign(myObject,EventTarget.prototype);
myObject.emit("somethingChanged");//"cc"
Object.assign()可以接收任意数量的源对象,并按指定顺序将属性复制到接收对象中,但是排位靠后的优先。
function first(){/*...*/}
first.prototype={
constructor:"first",
emit:function(){return "first";},
on:function(){/*...*/}
};
function second(){/*...*/}
first.prototype={
constructor:"second",
emit:function(){return "second";},
on:function(){/*...*/}
};
var myObject={};
mixin(myObject,first.prototype,second.prototype);
myObject.emit("somethingChanged");//"second"
重复的对象字面量属性
ES5严格模式中中,对象加入了对象字面量重复性的校检,当多个命名属性时会抛出错误。
而ES6中不会,ES6中会选取最后一个值。
//ES6
var person={
name:"cc",
name:"ccg"
}
console.log(person.name);//"ccg"
自有属性枚举顺序
ES5中未定义对象属性的枚举顺序,由JavaScript引擎厂商自行决定,然而ES6中严格规定了对象的自有属性被枚举的返回顺序。
自有属性的枚举顺序的基本规则是:
- 所有的数字键按升序排序。
- 所有的字符串键按照它们被加入对象的顺序排序。
- 所有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
增强对象原型
ES6对原型进行了改进。
改变对象的原型
正常情况下,对象原型在实例化后就无法改变了,但是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
简化原型访问的Super引用
Super可以更快捷的访问原型,请看例子。
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
//用super可以准确找到指向当前对象的原型
let friend={
getGreeting(){
return super().getGreeting()",hi";
}
};
Super在多重继承非常有用,请看例子
let person={
getGreeting(){
return "Hello";
}
};
//以person对象为原型
let friend={
getGreeting(){
return Object.getPrototypeOf(this).getGreeting.call(this)+",hi";
}
};
Object.setPrototypeOf(friend,person)
//原型是friend
let relative=Object.create(friend);
console.log(person.getGreeting());//"Hello"
console.log(friend.getGreeting());//"Hello,hi"
console.log(relative.getGreeting());//error!
//call(this)的定位会使程序进入递归调用,直到触发栈溢出报错
如果不使用call(this),程序会一级一级向上找,直到找到最大的构造函数Object。
let person={
getGreeting(){
return "Hello";
}
};
//以person对象为原型
let friend={
getGreeting(){
return Object.getPrototypeOf(this).getGreeting()+",hi";
}
};
Object.setPrototypeOf(friend,person);
//原型是friend
let relative=Object.create(friend);
console.log(person.getGreeting());//"Hello"
console.log(friend.getGreeting());//"Hello,hi"
console.log(relative.getGreeting());//"Hello,hi,hi"
最佳实践是使用Super,因为它不是动态变化的,总会指向正确的对象。
let person={
getGreeting(){
return "Hello";
}
};
//以person对象为原型
let friend={
getGreeting(){
return super.getGreeting()+",hi";
}
};
Object.setPrototypeOf(friend,person);
//原型是friend
let relative=Object.create(friend);
console.log(person.getGreeting());//"Hello"
console.log(friend.getGreeting());//"Hello,hi"
console.log(relative.getGreeting());//"Hello,hi"
正式的定义方法
ES6以前从未正式定义“方法”的概念,而在ES6中被正式定义为一个函数,这个函数内部包含[HomeObject]属性容纳这个方法从属的对象。
let person={
//是方法
getGreenting(){
return "Hello";//明确赋值person,[HomeObject]的属性值为person
}
};
//不是方法
function shareGreeting(){
return "Hi";//没有明确赋值给一个对象,[HomeObject]无法定义
}
值得注意的点是,Super的所有引用都是要通过[HomeObject]属性来确定后续的进程。第一步是在[HomeObject]属性值上调用Object.getPrototypeOf()来检索原型的引用率;然后搜索原型寻找同名函数;最后,设置this值并且调用相应的方法。
let person={
getGreenting(){
return "Hello";
}
};
let friend={
getGreenting(){
return super.getGreenting()+",hi!";
}
};
//以person对象为原型
Object.setPrototypeOf(friend,person);
console.log(friend.getGreenting());//Hello,hi!