1. Object.is()

用来比较两个值是否严格相等,与严格运算符(===)的行为基本一致。不同之处在于 +0 != -0 以及NaN等于自身。
**


2.Object.assign()

基本用法

Object.assign用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
Object.assign方法的第一个参数是目标对象,后面的参数都是源对象。
注意,如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。
如果只有一个参数,Object.assign会直接返回参数。
如果该参数不是对象,则会先转成对象,然后返回(undefined和null无法转换成对象)。
如果非对象参数出现在源对象的位置(非首参数),那么处理规则有所不同。首相,这些参数都会转成对象,如果无法转成对象 ,就会跳过。这意味着,如果undefinednull不在首参数,就不会报错。
**

  1. Object(true) //{[[PrimitiveValue]]:true}
  2. Object(10) //{[[PrimitiveValue]]:10}
  3. Object('abc') //{0:"a",1:"b",2:"c",length:3,[[PrimitiveValue]]:"abc"}

**
上面的代码中,布尔值、数值、字符串分别转成对应的包装对象,可以看到它们的原始值都在包装对象的内部属性[[PrimitiveValue]]上面,这个属性是不会被Object.assign拷贝的。只有字符串的包装对象,会产生可枚举的实义属性,那些属性则会被拷贝。

Object.assign拷贝的属性是有限制的,只拷贝源对象的自身属性(不拷贝继承属性),也不可拷贝不可枚举的属性。

  1. Object.assign({b:'c'}
  2. Object.defineProperty({},'invisible',{
  3. enumerable:false,
  4. value:'hello'
  5. })
  6. )
  7. //{b:'c'}

上面代码中,Object.assign要拷贝的对象只有一个不可枚举属性invisible,这个属性并没有被拷贝进去。

注意点

1.浅拷贝

Object.assign方法实行的是浅拷贝。

2.同名属性的替换

对于这种嵌套的对象,一旦遇到同名属性,Object.assign的处理方法是替换,而不是添加。

  1. const target = {a:{b:'c',d:'e'}}
  2. const source = {a:{b:'hello'}}
  3. Object.assign(target,source)
  4. //{a:{b:'hello'}}

上面代码中,target对象的a属性被source对象的a属性整个替换。

3.数组的处理

会把数组视为对象

  1. Object.assign([1,2,3],[4,5])
  2. //[4,5,3]

上面代码中,Object.assign把数组视为属性名为0、1、2的对象,因此源数组的0号属性4覆盖了目标数组的0号属性1

4.取值函数的处理

Object.assign只能进行值的复制,如果要复制的值是一个取值函数,那么将求值后再复制。

  1. const source = {
  2. get foo() {return 1}
  3. };
  4. const target = {};
  5. Object.assign(target,source)
  6. //{foo:1}

上面代码中,source对象的foo属性是一个取值函数,Object.assign不会复制这个取值函数,只会拿到值后,将这个值复制过去。


常见用途

1.为对象添加属性

  1. class Point{
  2. constructor(x,y){
  3. Object.assign(this,{x,y});
  4. }
  5. }

2.为对象添加方法

  1. Object.assign(SomeClass.prototype,{
  2. someMethod(arg1,arg2){
  3. ...
  4. },
  5. anothorMethod(){
  6. ...
  7. }
  8. });
  9. //等同于
  10. SomeClass.prototype.someMethod = function(arg1,arg2){
  11. ...
  12. };
  13. SomeClass.prototype.anotherMethod = function(){
  14. ...
  15. };

3.克隆对象

  1. function clone(origin){
  2. let originProto = Object.getPrototypeOf(origin);
  3. return Object.assign(Object.create(originProto),origin);
  4. }

4.合并多个对象

  1. const merge = (target,...sources)=>Object.assign(target,...sources);

5.为属性指定默认值

  1. const DEFAULTS = {
  2. logLevel = 0,
  3. outputFormat:'html'
  4. };
  5. function processContent(options){
  6. options = Object.assign({},DEFAULTS,options);
  7. console.log(options);
  8. //...
  9. }

注意,由于存在浅拷贝的问题,DEFAULTS对象和options对象的所有属性的值,最好都是简单类型,不要指向另一个对象。否则,DEFAULTS对象的属性很可能不起作用。
**

3.Object.getOwnPropertyDescriptors()

ES5的Object.getOwnPropertyDescriptors()方法会返回某个对象属性的描述对象(descriptor)。
Object.getOwnPropertyDescriptors()方法可以实现一个对象继承另一个对象。以前,继承另一个对象,常常写成下面这样。

  1. const obj = {
  2. __proto__: prot,
  3. foo: 123,
  4. };

ES6 规定__proto__只有浏览器要部署,其他环境不用部署。如果去除__proto__,上面代码就要改成下面这样。

  1. const obj = Object.create(prot);
  2. obj.foo = 123;
  3. // 或者
  4. const obj = Object.assign(
  5. Object.create(prot),
  6. {
  7. foo: 123,
  8. }
  9. );

有了Object.getOwnPropertyDescriptors(),我们就有了另一种写法。

  1. const obj = Object.create(
  2. prot,
  3. Object.getOwnPropertyDescriptors({
  4. foo: 123,
  5. })
  6. );

Object.getOwnPropertyDescriptors()也可以用来实现 Mixin(混入)模式。

  1. let mix = (object) => ({
  2. with: (...mixins) => mixins.reduce(
  3. (c, mixin) => Object.create(
  4. c, Object.getOwnPropertyDescriptors(mixin)
  5. ), object)
  6. });
  7. // multiple mixins example
  8. let a = {a: 'a'};
  9. let b = {b: 'b'};
  10. let c = {c: 'c'};
  11. let d = mix(c).with(a, b);
  12. d.c // "c"
  13. d.b // "b"
  14. d.a // "a"

上面代码返回一个新的对象d,代表了对象ab被混入了对象c的操作。
出于完整性的考虑,Object.getOwnPropertyDescriptors()进入标准以后,以后还会新增Reflect.getOwnPropertyDescriptors()方法。

4.proto属性,Object.setPrototypeOf(),Object.getPrototypeOf()

JavaScript 语言的对象继承是通过原型链实现的。ES6 提供了更多原型对象的操作方法。

proto属性

__proto__属性(前后各两个下划线),用来读取或设置当前对象的prototype对象。目前,所有浏览器(包括 IE11)都部署了这个属性。

  1. // es5 的写法
  2. const obj = {
  3. method: function() { ... }
  4. };
  5. obj.__proto__ = someOtherObj;
  6. // es6 的写法
  7. var obj = Object.create(someOtherObj);
  8. obj.method = function() { ... };

该属性没有写入 ES6 的正文,而是写入了附录,原因是__proto__前后的双下划线,说明它本质上是一个内部属性,而不是一个正式的对外的 API,只是由于浏览器广泛支持,才被加入了 ES6。标准明确规定,只有浏览器必须部署这个属性,其他运行环境不一定需要部署,而且新的代码最好认为这个属性是不存在的。因此,无论从语义的角度,还是从兼容性的角度,都不要使用这个属性,而是使用下面的Object.setPrototypeOf()(写操作)、Object.getPrototypeOf()(读操作)、Object.create()(生成操作)代替。
实现上,__proto__调用的是Object.prototype.__proto__,具体实现如下。

  1. Object.defineProperty(Object.prototype, '__proto__', {
  2. get() {
  3. let _thisObj = Object(this);
  4. return Object.getPrototypeOf(_thisObj);
  5. },
  6. set(proto) {
  7. if (this === undefined || this === null) {
  8. throw new TypeError();
  9. }
  10. if (!isObject(this)) {
  11. return undefined;
  12. }
  13. if (!isObject(proto)) {
  14. return undefined;
  15. }
  16. let status = Reflect.setPrototypeOf(this, proto);
  17. if (!status) {
  18. throw new TypeError();
  19. }
  20. },
  21. });
  22. function isObject(value) {
  23. return Object(value) === value;
  24. }

如果一个对象本身部署了__proto__属性,该属性的值就是对象的原型。

  1. Object.getPrototypeOf({ __proto__: null })
  2. // null

Object.setPrototypeOf()

Object.setPrototypeOf方法的作用与__proto__相同,用来设置一个对象的prototype对象,返回参数对象本身。它是 ES6 正式推荐的设置原型对象的方法。

  1. // 格式
  2. Object.setPrototypeOf(object, prototype)
  3. // 用法
  4. const o = Object.setPrototypeOf({}, null);

该方法等同于下面的函数。

  1. function setPrototypeOf(obj, proto) {
  2. obj.__proto__ = proto;
  3. return obj;
  4. }

下面是一个例子。

  1. let proto = {};
  2. let obj = { x: 10 };
  3. Object.setPrototypeOf(obj, proto);
  4. proto.y = 20;
  5. proto.z = 40;
  6. obj.x // 10
  7. obj.y // 20
  8. obj.z // 40

上面代码将proto对象设为obj对象的原型,所以从obj对象可以读取proto对象的属性。
如果第一个参数不是对象,会自动转为对象。但是由于返回的还是第一个参数,所以这个操作不会产生任何效果。

  1. Object.setPrototypeOf(1, {}) === 1 // true
  2. Object.setPrototypeOf('foo', {}) === 'foo' // true
  3. Object.setPrototypeOf(true, {}) === true // true

由于undefinednull无法转为对象,所以如果第一个参数是undefinednull,就会报错。

  1. Object.setPrototypeOf(undefined, {})
  2. // TypeError: Object.setPrototypeOf called on null or undefined
  3. Object.setPrototypeOf(null, {})
  4. // TypeError: Object.setPrototypeOf called on null or undefined

Object.getPrototypeOf()

该方法与Object.setPrototypeOf方法配套,用于读取一个对象的原型对象。

  1. Object.getPrototypeOf(obj);

下面是一个例子。

  1. function Rectangle() {
  2. // ...
  3. }
  4. const rec = new Rectangle();
  5. Object.getPrototypeOf(rec) === Rectangle.prototype
  6. // true
  7. Object.setPrototypeOf(rec, Object.prototype);
  8. Object.getPrototypeOf(rec) === Rectangle.prototype
  9. // false

如果参数不是对象,会被自动转为对象。

  1. // 等同于 Object.getPrototypeOf(Number(1))
  2. Object.getPrototypeOf(1)
  3. // Number {[[PrimitiveValue]]: 0}
  4. // 等同于 Object.getPrototypeOf(String('foo'))
  5. Object.getPrototypeOf('foo')
  6. // String {length: 0, [[PrimitiveValue]]: ""}
  7. // 等同于 Object.getPrototypeOf(Boolean(true))
  8. Object.getPrototypeOf(true)
  9. // Boolean {[[PrimitiveValue]]: false}
  10. Object.getPrototypeOf(1) === Number.prototype // true
  11. Object.getPrototypeOf('foo') === String.prototype // true
  12. Object.getPrototypeOf(true) === Boolean.prototype // true

如果参数是undefinednull,它们无法转为对象,所以会报错。

  1. Object.getPrototypeOf(null)
  2. // TypeError: Cannot convert undefined or null to object
  3. Object.getPrototypeOf(undefined)
  4. // TypeError: Cannot convert undefined or null to object

5.Object.keys(),Object.values(),Object.entries()

Object.keys()

ES5 引入了Object.keys方法,返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。

  1. var obj = { foo: 'bar', baz: 42 };
  2. Object.keys(obj)
  3. // ["foo", "baz"]

ES2017 引入了跟Object.keys配套的Object.valuesObject.entries,作为遍历一个对象的补充手段,供for...of循环使用。

  1. let {keys, values, entries} = Object;
  2. let obj = { a: 1, b: 2, c: 3 };
  3. for (let key of keys(obj)) {
  4. console.log(key); // 'a', 'b', 'c'
  5. }
  6. for (let value of values(obj)) {
  7. console.log(value); // 1, 2, 3
  8. }
  9. for (let [key, value] of entries(obj)) {
  10. console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]
  11. }

Object.values()

Object.values方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。

  1. const obj = { foo: 'bar', baz: 42 };
  2. Object.values(obj)
  3. // ["bar", 42]

返回数组的成员顺序,与本章的《属性的遍历》部分介绍的排列规则一致。

  1. const obj = { 100: 'a', 2: 'b', 7: 'c' };
  2. Object.values(obj)
  3. // ["b", "c", "a"]

上面代码中,属性名为数值的属性,是按照数值大小,从小到大遍历的,因此返回的顺序是bca
Object.values只返回对象自身的可遍历属性。

  1. const obj = Object.create({}, {p: {value: 42}});
  2. Object.values(obj) // []

上面代码中,Object.create方法的第二个参数添加的对象属性(属性p),如果不显式声明,默认是不可遍历的,因为p的属性描述对象的enumerable默认是falseObject.values不会返回这个属性。只要把enumerable改成trueObject.values就会返回属性p的值。

  1. const obj = Object.create({}, {p:
  2. {
  3. value: 42,
  4. enumerable: true
  5. }
  6. });
  7. Object.values(obj) // [42]

Object.values会过滤属性名为 Symbol 值的属性。

  1. Object.values({ [Symbol()]: 123, foo: 'abc' });
  2. // ['abc']

如果Object.values方法的参数是一个字符串,会返回各个字符组成的一个数组。

  1. Object.values('foo')
  2. // ['f', 'o', 'o']

上面代码中,字符串会先转成一个类似数组的对象。字符串的每个字符,就是该对象的一个属性。因此,Object.values返回每个属性的键值,就是各个字符组成的一个数组。
如果参数不是对象,Object.values会先将其转为对象。由于数值和布尔值的包装对象,都不会为实例添加非继承的属性。所以,Object.values会返回空数组。

  1. Object.values(42) // []
  2. Object.values(true) // []

Object.entries()

Object.entries()方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。

  1. const obj = { foo: 'bar', baz: 42 };
  2. Object.entries(obj)
  3. // [ ["foo", "bar"], ["baz", 42] ]

除了返回值不一样,该方法的行为与Object.values基本一致。
如果原对象的属性名是一个 Symbol 值,该属性会被忽略。

  1. Object.entries({ [Symbol()]: 123, foo: 'abc' });
  2. // [ [ 'foo', 'abc' ] ]

上面代码中,原对象有两个属性,Object.entries只输出属性名非 Symbol 值的属性。将来可能会有Reflect.ownEntries()方法,返回对象自身的所有属性。
Object.entries的基本用途是遍历对象的属性。

  1. let obj = { one: 1, two: 2 };
  2. for (let [k, v] of Object.entries(obj)) {
  3. console.log(
  4. `${JSON.stringify(k)}: ${JSON.stringify(v)}`
  5. );
  6. }
  7. // "one": 1
  8. // "two": 2

Object.entries方法的另一个用处是,将对象转为真正的Map结构。

  1. const obj = { foo: 'bar', baz: 42 };
  2. const map = new Map(Object.entries(obj));
  3. map // Map { foo: "bar", baz: 42 }

自己实现Object.entries方法,非常简单。

  1. // Generator函数的版本
  2. function* entries(obj) {
  3. for (let key of Object.keys(obj)) {
  4. yield [key, obj[key]];
  5. }
  6. }
  7. // 非Generator函数的版本
  8. function entries(obj) {
  9. let arr = [];
  10. for (let key of Object.keys(obj)) {
  11. arr.push([key, obj[key]]);
  12. }
  13. return arr;
  14. }

6.Object.fromEntries()

Object.fromEntries()方法是Object.entries()的逆操作,用于将一个键值对数组转为对象。

  1. Object.fromEntries([
  2. ['foo', 'bar'],
  3. ['baz', 42]
  4. ])
  5. // { foo: "bar", baz: 42 }

该方法的主要目的,是将键值对的数据结构还原为对象,因此特别适合将 Map 结构转为对象。

  1. // 例一
  2. const entries = new Map([
  3. ['foo', 'bar'],
  4. ['baz', 42]
  5. ]);
  6. Object.fromEntries(entries)
  7. // { foo: "bar", baz: 42 }
  8. // 例二
  9. const map = new Map().set('foo', true).set('bar', false);
  10. Object.fromEntries(map)
  11. // { foo: true, bar: false }

该方法的一个用处是配合URLSearchParams对象,将查询字符串转为对象。

  1. Object.fromEntries(new URLSearchParams('foo=bar&baz=qux'))
  2. // { foo: "bar", baz: "qux" }