在第 5 篇中, 讲解了多个对象字面量的改进, 本节将重点介绍两个新增的静态方法, 以及对象属性的重复处理和枚举顺序.
一, Object.is()
此方法用于判断两个值是否相同, 内部实现了 SameValue 算法, 其行为类似于全等 (===) 比较, 但它认为两个 NaN 是相等的, 而 + 0 和 - 0 却是不等的. Object.is()和全等的区别如下所示.
- NaN === NaN; //false
- Object.is(NaN, NaN); //true
- +0 === -0; //true
- Object.is(+0, -0); //false
二, Object.assign()
此方法可将多个对象合并成一个, 它的第一个参数是目标对象, 剩余的参数都是源对象, 返回值是最终的目标对象, 接下来会列举出此方法的 6 个特点.
(1)只能拷贝可枚举的自有属性 (定义在对象中), 无法拷贝继承属性(定义在对象原型中) 和不可枚举的属性. 以下面的代码为例, obj2 对象中的 name 是继承属性, age 是不可枚举的属性, 只有 school 是可枚举的自有属性, 因此只有 school 属性拷贝到了空对象中.
- var obj1 = { name: "strick" },
- obj2 = Object.create(obj1); //name 是继承属性
- obj2.age = 28; //age 是不可枚举的属性
- Object.defineProperty(obj2, "age", {
- enumerable: false
- });
- obj2.school = "university"; //school 是可枚举的自有属性
- Object.assign({}, obj2); //{school: "university"}
(2)遇到同名的属性, 后面的会覆盖之前的. 例如下面的两个对象 obj1 和 obj2 都包含 name 属性, obj1 在 obj2 之前传到方法中, 最终 obj1 对象的 name 属性将被覆盖掉.
- var obj1 = { name: "strick" },
- obj2 = { name: "freedom" };
- Object.assign({}, obj1, obj2); //{name: "freedom"}
(3)Object.assign()执行的是浅拷贝, 如果属性的值是对象, 那么只会拷贝引用该对象的指针. 在下面的代码中, obj1 对象的 man 属性是一个对象, 将其与空对象合并, 然后把返回值赋给 obj2 变量, 再修改 obj1 中的 name 属性, 由于是浅拷贝, 因此 obj2 中的 name 属性也会受影响.
- var obj1 = { man: { name: "strick" } },
- obj2 = Object.assign({}, obj1);
- obj1.man.name = "freedom";
- console.log(obj2); //{man: {name: "freedom"}}
(4)Symbol 类型的属性也能被拷贝. Symbol 是 ES6 引入的第 6 种基本类型, 可以像字符串那样作为对象的属性名, 具体如下所示.
- var obj1 = { [Symbol("name")]: "strick" },
- obj2 = Object.assign({}, obj1);
- console.log(obj2); //{Symbol(name): "strick"}
(5)当源对象的位置是基本类型的值时, 它们会被包装成对象, 再进行合并. 但由于 undefined 和 null 没有包装对象, 并且数字和布尔值的包装对象又没有可枚举的属性, 因此只有字符串的包装对象才能不被忽略, 最终以数组的形式拷贝到目标对象中, 如下所示.
- var obj = Object.assign({
- }, 1, "a", true, undefined, null);
- console.log(obj); //{
- 0: "a"
- }
(6)源对象的访问器属性会变成目标对象的数据属性. 如下代码所示, obj 对象包含一个名为 name 的访问器属性, 在把它与空对象合并后, 目标对象会有一个名为 name 的数据属性, 其值就是访问器属性中 get()方法的返回值.
- var obj = {
- get name() {
- return "strick";
- }
- };
- Object.assign({}, obj); //{name: "strick"}
三, 重复属性
在 ES5 的严格模式中, 重复的属性名会引起语法错误. 但 ES6 不会再做这个检查, 当出现重复属性时, 排在后面的同名属性将覆盖前面的, 即属性值以后面的为准, 执行过程如下所示.
- var obj = {
- name: "strick",
- name: "freedom"
- };
- console.log(obj.name); //"freedom"
四, 枚举顺序
ES6 规定了自有属性的枚举顺序, 并且会将同一类别的属性整合到一块, 具体的排列规则如下所列:
(1)首先遍历数字类型或数字字符串的属性, 按大小升序排列.
(2)接着遍历字符串类型的属性, 按添加时间的先后顺序排列.
(3)最后遍历符号类型的属性, 也按添加顺序排列.
for-in 循环, JSON 对象的序列化方法 stringify(),Object 对象的 getOwnPropertyNames(),keys(),getOwnPropertySymbols()以及新引入的 assign()方法在执行过程中都会遵循这套新的排列规则, 具体如下所示.
- var obj = {
- c: 1,
- 1: 2,
- a: 3,
- "0": 4,
- [Symbol("x")]: 5,
- [Symbol("y")]: 6
- };
- var properties = [];
- for(var key in obj) {
- if(obj.hasOwnProperty(key)) { // 过滤掉继承属性
- properties.push(key);
- }
- }
- console.log(properties); //["0", "1", "c", "a"]
- JSON.stringify(obj); //{"0":4,"1":2,"c":1,"a":3}
- Object.getOwnPropertyNames(obj); //["0", "1", "c", "a"]
- Object.keys(obj); //["0", "1", "c", "a"]
- Object.getOwnPropertySymbols(obj); //[Symbol(x), Symbol(y)]
- Object.assign({}, obj); //{0: 4, 1: 2, c: 1, a: 3, Symbol(x): 5, Symbol(y): 6}