可计算属性名
现在,属性可以在字面量中直接写
let p = "a b c";
let a = {[p]: '123';
}
ES5 不能直接写,只能这么写:
var p = "a b c";
var a = { };
a[p] = '1 2 3';
新增方法
Object.is()
严格相等运算 ===
不会触发强制类型转换,但是也有不准确的时候。
+0 === -0 // true
NaN === NaN // false
在 JS 中,-0
和 +0
是不同的实体。应该是 false。NaN 与自身比较应该是 true。
ES6 引入了 Object.is()
弥补全局运算符的不准确。
===
与 Object.is()
的唯一区别就是 +0,-0 ,NaN
这两种情况。
Object.is(+0, -0) //false
Object.is(NaN, NaN) // true
当你的严格相等代码中,有可能出现以上两种情况,考虑使用 Object.is
Object.assign
混合(mixin)是 JS 中对象组合的常见模式。
例如
function mixin(receiver, supplier){
Object.keys(supplier).forEach(function(key)) {
receiver[key] = supplier[key];
});
return receiver;
}
mixin 函数遍历 supplier 的自有属性并复制到 receiver 中(浅复制)。使得 receiver 可以不通过继承,获得属性。
function EventTarget() {}
EventTarget.prototype = {
constructor: EventTarget,
emit: function(){},
on: function(){}
};
var myObj = {};
mixin(myObj, EventTarget.prototype);
myObj.emit("somethingChanged");
这里,myObj 接收了 EventTarget.prototype 对象的所有行为,从而可以使用 emit 发布事件,可以通过 on 订阅事件。
ES6 的 Object.assign
方法实现了上面的 mixin 的功能
Object.assign(接收对象,源对象 1,源对象 2,源对象 3。。。)
注意,assign 不能复制访问器属性(getter 和 setter),并且访问器属性的 enumerable
是 true 时才能被 assign 方法发现,如下所示:
// assign 不能复制访问器属性
let supplier = {
age: 18,
}
Object.defineProperty(supplier, 'year', {
get() {
console.log('调用了 get');
return this.age;
},
set(value) {
console.log('set 被调用了');
this.age = value;
},
enumerable: true,
})
supplier.year = 2022;
// 都改成了 2022
console.log(supplier.age);
console.log(supplier.year);
let myObj = {};
Object.assign(myObj, supplier);
console.log(myObj.age);
console.log(myObj.year);
myObj.year = 2021;
// age 没有变,说明 assign 没有复制访问器属性(getter 和 setter)
console.log(myObj.age);
console.log(myObj.year);
supplier 的访问器属性会变为接收器对象的一个数据属性。不再是访问器属性。
assign 可以接 N 个源对象,排名靠后的对象会覆盖前面的同名属性。
字面量对象的重复属性
ES6 字面量中重复定义的属性,不会报错,不管是严格和非严格模式。
值取最后定义的。
自有属性的枚举顺序
ES6 严格规定明确了自有属性被枚举时的返回顺序。
被影响的方法:Obejct.assign, Object.getOwnPropertyNames,Reflect.ownKeys 等等。
ES6 没有明确 for-in
循环的属性枚举顺序。Object.keys 和 JSON.stringify 与 for-in 顺序相同,因此 ES6 也没有明确。(Chrome V100 的顺序与 ES 的枚举顺序一致)
顺序规则:
- 数字 key 按升序排序
- 字符串 key 按照 ** 被加入对象的顺序 ** 排序
- 所有 symbol 按照 ** 被加入对象的顺序 ** 排序
let obj = {
0: 1,
3: 1,
bz: 1,
b: 1,
bb: 1,
az: 1,
ab: 1,
1: 1,
"-2": 1,
"-1": 1
}
obj[2] = 1;
obj[-0] = 1;
obj[-1] = 1;
console.log(Object.getOwnPropertyNames(obj).join('-'));
// Chrome 100 结果:0-1-2-3-bz-b-bb-az-ab--2--1-a
- 对于数字,在枚举时被重新排序。
- 字符串键在数字键后面。顺序是声明顺序和插入对象的顺序。
- 负数只按照前面的 “-” 排序,视为字符串。
增强的对象原型
一般情况,无论是构造函数,还是 Object.create 方法创建的对象,原型都是在对象被创建时指定的。
ES5 的原则是,在实例化之后,对象原型保持不变。但是 ES5 缺少实例化后改变原型的标准方法。ES5 的 Object.getPrototypeOf()
用来返回指定对象的原型。
在 ES6,又新增了 Obejct.setPrototypeOf()
方法,用来改变指定对象的原型。
代码如下:
let person = {
getGreeting() {
return 'hello';
}
}
let dog = {
getGreeting() {
return 'baibai';
}
}
let friend = Object.create(person);
console.log(friend.getGreeting()); // hello
console.log(Object.getPrototypeOf(friend) === person); //true
Object.setPrototypeOf(friend, dog);
console.log(friend.getGreeting()); // baibai
console.log(Object.getPrototypeOf(friend) === dog); //true
用来简化原型访问的 Super
在 ES5,你想重写对象实例的方法,又需要调用与它同名的原型方法,代码如下:
// 在 ES5,你想重写对象实例的方法,又需要调用与它同名的原型方法,代码如下:
let person = {
id: '#human',
getGreeting() {
return this.id + 'hello';
}
}
let dog = {
id: 'animal',
getGreeting() {
return this.id + 'baibai';
}
}
let friend = {
id: '#friend',
getGreeting() {
return Object.getPrototypeOf(this).getGreeting.call(this) + ", hi!";
}
}
Object.setPrototypeOf(friend, person);
console.log(friend.getGreeting()); // #friend hello, hi!
Object.getPrototypeOf(this)
确保得到的是 this(friend)的原型 –person。后面的 call 可以确保 this 的指向的是 friend。如果不加 call,最后打印 #human hello, hi!
ES6 的 super 引用相当于指向对象原型的指针,相当于 Object.getPrototypeOf(this),
代码如下:
let friend = {
id: '#friend',
getGreeting() {
return super.getGreeting() + ", hi!";
}
}
Object.setPrototypeOf(friend, person);
console.log(friend.getGreeting()); // #friend hello, hi!
super 不能在非简写方法中使用,会报错
SyntaxError: 'super' keyword unexpected here
:
let friend = {
id: '#friend',
getGreeting: function () {
return super.getGreeting() + ", hi!";
}
}
多重继承时,用 ES5 的 Object.getPrototypeOf(this)会出现循环引用的问题,代码如下:
// super 对于多重继承很有用
let friend = {
id: '#friend',
getGreeting() {
// ES5 的方式在多重继承时会有问题,造成循环调用
console.log(this.id);
return Object.getPrototypeOf(this).getGreeting.call(this) + ", hi!";
}
}
Object.setPrototypeOf(friend, person);
// relative 原型是 friend
let relative = Object.create(friend);
console.log(relative.getGreeting());
RangeError: Maximum call stack size exceeded。
因为 relative.getGreeting()
是调用的 friend
的 getGreeting
方法,Object.getPrototypeOf(this)
中的 this
指向的是 relative
,调用 Object.getPrototypeOf(this)
的结果返回的就是 friend
对象,此时造成 friend.getGreeting
的循环调用。
使用 super,以上问题迎刃而解,代码如下:
let friend = {
id: '#friend',
getGreeting() {
// ES5 的方式在多重继承时会有问题,造成循环调用
console.log(this.id);
return super.getGreeting() + ", hi!";
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论。