## Object.is()
ES5比较两个值是否相等:相等运算符(`==`)和严格相等运算符(`===`)。前者会自动转换数据类型,后者的`NaN`不等于自身,以及`+0`等于`-0`。`Object.is`一是`+0`不等于`-0`,二是`NaN`等于自身。
```js
Object.is('foo', 'foo') // true
Object.is({}, {}) // false
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
```
## Object.assign()
`Object.assign`方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
```js
var target = { a: 1, b: 1 };
var source1 = { b: 2, c: 2 };
var source2 = { c: 3 };
Object.assign(target, source1, source2);
console.log(target) // {a:1, b:2, c:3}
//如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性,只有一个参数,直接返回该参数。
```
由于`undefined`和`null`无法转成对象,所以如果它们作为参数,就会报错。
```
Object.assign(undefined) // 报错
Object.assign(null) // 报错
```
**注意:**`Object.assign`方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。
```js
var obj1 = {a: {b: 1}};
var obj2 = Object.assign({}, obj1);
obj1.a.b = 2;
obj2.a.b // 2
```
对于这种嵌套的对象,一旦遇到同名属性,`Object.assign`的处理方法是替换,而不是添加。
**Lodash的`_.defaultsDeep`方法**,可以解决浅拷贝的问题,得到深拷贝的合并。
```js
var target = { a: { b: 'c', d: 'e' } }
var source = { a: { b: 'hello' } }
Object.assign(target, source)
// { a: { b: 'hello' } }
```
## Object.keys()
返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键名。
```js
var obj = { foo: "bar", baz: 42 };
Object.keys(obj)
// ["foo", "baz"]
```
## Object.values()
`Object.values`方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键值。
```js
var obj = { foo: "bar", baz: 42 };
Object.values(obj)
// ["bar", 42]
```
```js
var obj = { 100: 'a', 2: 'b', 7: 'c' };
Object.values(obj)
// ["b", "c", "a"],
//返回数组的成员顺序,与《属性的遍历》部分介绍的排列规则一致。
```
## Object.entries
`Object.entries`方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键值对数组。
```js
var obj = { foo: 'bar', baz: 42 };
Object.entries(obj)
// [ ["foo", "bar"], ["baz", 42] ]
```
`Object.entries`方法的一个用处是,将对象转为真正的`Map`结构。
```js
var obj = { foo: 'bar', baz: 42 };
var map = new Map(Object.entries(obj));
console.log(map) // Map { foo: "bar", baz: 42 }
```
## 属性名表达式
```js
var lastWord = 'last word';
var a = {
'first word': 'hello',
[lastWord]: 'world'
};
a['first word'] // "hello"
a[lastWord] // "world"
a['last word'] // "world"
```
## 属性的遍历
ES6一共有5种方法可以遍历对象的属性。
**(1)for...in**
`for...in`循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)。
**(2)Object.keys(obj)**
`Object.keys`返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)。
**(3)Object.getOwnPropertyNames(obj)**
`Object.getOwnPropertyNames`返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性)。
**(4)Object.getOwnPropertySymbols(obj)**
`Object.getOwnPropertySymbols`返回一个数组,包含对象自身的所有Symbol属性。
**(5)Reflect.ownKeys(obj)**
`Reflect.ownKeys`返回一个数组,包含对象自身的所有属性,不管是属性名是Symbol或字符串,也不管是否可枚举。
**以上的5种方法遍历对象的属性,都遵守同样的属性遍历的次序规则。**
- 首先遍历所有属性名为数值的属性,按照数字排序。
- 其次遍历所有属性名为字符串的属性,按照生成时间排序。
- 最后遍历所有属性名为Symbol值的属性,按照生成时间排序。
## 对象的扩展运算符
**Rest解构赋值**
```js
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
console.log(x) // 1
console.log(y) // 2
console.log(z) // { a: 3, b: 4 }
```
```
let { x, y, ...z } = null; // 运行时错误
let { x, y, ...z } = undefined; // 运行时错误
```
```
let { ...x, y, z } = obj; // 句法错误
let { x, ...y, ...z } = obj; // 句法错误
```
**注意:**Rest解构赋值的拷贝是浅拷贝,即如果一个键的值是复合类型的值(数组、对象、函数)、那么Rest解构赋值拷贝的是这个值的引用,而不是这个值的副本。
```js
let obj = { a: { b: 1 } };
let { ...x } = obj;
obj.a.b = 2;
x.a.b // 2
```