扩展对象的功能
- 普通对象 具有js对象所有默认内部行为的
- 特异对象 具有某些与默认行为不符的内部行为
- 标准对象 es6规范中定义的对象,Array/Date等
- 内建对象 脚本开始执行时存在于javascript执行环境的对象,所有标准对象都是内建对象
对象字面量语法扩展
- 属性初始值的简写,当一个对象的属性与本地变量同名时,不必再写冒号和值
- 对象方法的简写语法
- 可计算属性名
// javascript 引擎会在可访问作用域中查找其同名变量;找到,则该量变的值被赋给对象字面量里的同名属性,在现在的js开发中,为对象字面量的属性赋同名局部变量的值是一种常见的做法,这种简写方法有助于消除命名错误
function createPerson(name,age){
return {
name,
age
}
}
// 对象方法的简写 消除冒号和function关键字。如下
var person = {
name:"Nicholas",
sayName(){
console.log(this.name)
}
}
// 通过对象方法简写语法创建的方法有一个name属性,其值为小括号前的名称,上例中person.sayName()方法的name属性的值为"sayName"
es5有些属性名要通过[]
方括号来设置和访问其值,如
var person = {},lastName = "last name"
person["first name"] = "Nicholas"
person[lastName] = "Zakas"
console.log(person["first name"]); // Nicholas
console.log(person[lastName]); //Zakas
因为属性名称中都含有空格,因而不可使用点的方式引用,却可以使用方括号,因为它支持通过任何字符串值作为名称访问属性值。
在对象字面量中,可以直接使用字符串字面量作为属性名称,如下
var person = {
"first name": "Nicholas"
}
console.log(person["first name"]); // "Nicholas"
// Es5 无法为通过计算得到的变量值,作为对象的字面量定义该属性
// es6 通过中括号方式可以定义这样的字面量
let lastName = "last name";
let person = {
"first name": "Nicholas",
[lastName]:"Zakas"
}
console.log(person["first name"]); // Nicholas
console.log(person[lastName]); // Zakas
console.log(person["last name"]) // Zakas
Object.is()
该方法来弥补全等运算的不准确运算。这个方法接受两个参数,如果这两个参数类型相同有相同的值,则返回true。
Object.is(NaN,NaN) // true
NaN === Nan //false
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) //true
console.log(Object.is(NaN,Nan)) //true
console.log(Object.is(5,5)) //true
console.log(Object.is(5,"5")) //false
Object.assign()
混合(Mixin)是javascript中实现对象组合最流行的一种模式。在一个mixin方法中,一个对象接收来自另一个对象的属性和方法,许多javascript库中都有类似的mixin方法:
// 浅复制
function minxin(receiver, supplier){
Object.keys(supplier).forEach(function(key){
receiver[key] = supplier[key]
})
return receiver
}
Object.assign()
方法来实现相同的功能,这个方法接受一个接收对象和任意数量的源对象,mixin()
方法使用赋值操作符=
来复制相关属性,却不能复制访问器属性到接收对象中,因此最终添加的方法弃用mixin
而改用assign作为方法名。
Object.assign()
方法可以接受任意数量的源对象,并按指定顺序将属性复制到接收对象中。如果多个源对象具有同名属性,则排位靠后的源对象会覆盖排位靠前的。
Object.assign()
方法不能将提供者的访问器属性复制到接收对象中。由于Object.assign()方法执行了赋值操作,因此提供者的访问器属性终会转变为接收对象中的一个数据属性。
var receiver = {},supplier = {
get name() {
return "file.js"
}
};
Object.assign(receiver,supplier);
var descriptor = Object.getOwnPropertyDescriptior(receiver,"name")
console.log(descriptor.value); //file.js
console.log(descriptor.get); // undefined
重复的对象字面量属性
es5 属性重名 会报错,es6不再做这样强制的约束,对于每一组重复属性,都会取最后一个取值。
自有属性枚举顺序
es6严格规定了对象的自有属性被枚举时的返回顺序,这会影响到Object.getOwnPropertyName()
方法及Reflect.ownKeys
返回属性的方式,Object.assign()
方法处理属性的顺序也将随之改变,规则如下:
- 所有数字键按升序排序
- 所有字符串键按照它们被加入对象的顺序排序
- 所有symbol键,按照它们被加入对象的顺序排序。
增强对象原型
es5都是javascript编程最重要的设定之一,虽然在es5中添加了Object.getPrototypeOf()方法来返回任意对象的原型,但仍缺少对象在实例化后改变原型的标准方法。es6中添加了Object.setPrototypeOf()
方法来改变这一现状,通过这一方法可以改变任意指定对象的原型,它接受两个参数:被改变原型的对象及替代第一个参数原型对象。
let person={
getGreeting(){
return "Hello";
}
}
let dog = {
getGreeting(){
return "Woof";
}
}
// 以person对象为原型
let friend = Ojbect.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()方法返回储存在其中的值,调用Ojbect.setPrototypeOf()
方法改变其中的值。然而,这不是操作[[Prototype]]
值的唯一方法。
- 我们需要牢记两点:①
__proto__
和constructor
属性是对象所独有的;②prototype
属性是函数所独有的,因为函数也是一种对象,所以函数也拥有__proto__
和constructor
属性。 __proto__
属性的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__
属性所指向的那个对象(父对象)里找,一直找,直到__proto__
属性的终点null,再往上找就相当于在null上取值,会报错。通过__proto__
属性将对象连接起来的这条链路即我们所谓的原型链。prototype
属性的作用就是让该函数所实例化的对象们都可以找到公用的属性和方法,即f1.__proto__ === Foo.prototype
。constructor
属性的含义就是指向该对象的构造函数,所有函数(此时看成对象了)最终的构造函数都指向Function。
简化原型访问的Super作用
子类方法覆写父类方法,还要调用传参给父类方法; ECMAScript 6
引入了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
es6简化了这种写法:
let friend = {
getGreeting(){
// return Object.getPrototypeOf(this).getGreeting.call(this)+', hi!'
return super.getGreeting()+", hi~"
}
}
// 注意要使用简写方法对象中使用super引用,如下会报错
let friend = {
getGreeting:function(){
// return Object.getPrototypeOf(this).getGreeting.call(this)+', hi!'
return super.getGreeting()+", hi~"
}
}
super
引用不是动态变化的,它总是指向正确的对象,在这个示例中,无论有多少其他方法继承了getGreeting方法,super.getGreeting()始终指向
正式的方法定义
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绑定并且调用相应的方法。
// 自己分析一下
let person={
getGreeting(){
return "Hello";
}
}
let friend = {
getGreeting(){
// return Object.getPrototypeOf(this).getGreeting.call(this)+', hi!'
return super.getGreeting()+", hi~"
}
}
Object.setPrototypeOf(friend,person)
console.log(friend.getGreeting()) // "Hello hi~"
总结
- 属性
- 简化属性定义语法,使将当前作用域中的同名变量赋值给对象的语法变得更加简洁;
- 添加可计算属性名特性,允许为对象指定非字面量属性名;
- 添加对象方法简写语法,在对象字面量中定义方法时可以省略冒号和function关键字
- es6严格模式下对象字面量重复名称校验,即使在同一个对象字面量中定义两个同名属性也不会抛出错误
Object.assign()
方法可以一次性更改对象中的多个属性,如果使用混入(mixin)模式这将非常有用Object.is()
方法对于所有值进行严格等价判断,当将其用于处理javascript值问题时比===
更加安全Object.setPrototypeOf()
方法,对象被创建后修改它的原型super
关键字调用原型上的方法,此时的this绑定会被自动设置为当前作用域的this值
主题测试文章,只做测试使用。发布者:Walker,转转请注明出处:https://joyjs.cn/archives/4329