ES6 笔记之第十一章 对象的新增方法


1. Object.is()

  1. ES5 :
  • 相等运算符(==) :会自动转换数据类型
  • 严格相等运算符(===):NaN不等于自身,+0等于-0
  1. ES6 :
  • Object.is() : 比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。

  • 不同之处只有两个:一是+0不等于-0,二是NaN等于自身。

    Object.is({}, {})// false
    
    +0 === -0 //true
    NaN === NaN // false
    
    Object.is(+0, -0) // false
    Object.is(NaN, NaN) // true

2. Object.assign()

2.1 基本用法

Object.assign()方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。该方法的第一个参数是目标参数,其余都是源对象。
注意 如果目标对象和源对象有一个或者多个同名属性,则后边的属性会覆盖前面的属性

  • 如果Object.is()方法只有一个对象参数,会直接返回该参数

  • 如果Object.is()方法的参数不是对象,先转成对象再返回

  • 由于undefined和null无法转为对象,如果参数是他们就会报错

  • 其他类型的值(即数值、字符串和布尔值)不在首参数,也不会报错。但是,除了字符串会以数组形式,拷贝入目标对象,其他值都不会产生效果。

    const v1 = 'abc';
    const v2 = true;
    const v3 = 10;
    
    const obj = Object.assign({}, v1, v2, v3);
    console.log(obj); // { "0": "a", "1": "b", "2": "c" }

    上面代码中,v1、v2、v3分别是字符串、布尔值和数值,结果只有字符串合入目标对象(以字符数组的形式),数值和布尔值都会被忽略。这是因为只有字符串的包装对象,会产生可枚举属性。

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

    Object.assign({b: 'c'},
      Object.defineProperty({}, 'invisible', {
        enumerable: false,
        value: 'hello'
      })
    )
    // { b: 'c' }

    上述代码中,要拷贝的对象只有不可枚举的invisible,因为不可枚举,所以没有被拷贝进去。

  • 属性名为Symbol值得属性,也会被拷贝

    Object.assign({ a: 'b' }, { [Symbol('c')]: 'd' })
    // { a: 'b', Symbol(c): 'd' }

    2.2 注意点

    (1)浅拷贝

    Object.assign()方法实行的是浅拷贝,而不是深拷贝。(浅拷贝仅是对对象的引用,会随之改变而改变)

(2)同名属性的替换

对于嵌套的对象,遇到同名属性时,处理方法时替换而不是添加。

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

而不会得到{ a: { b: 'hello', d: 'e' } }的结果。

(3)数组的处理

Object.assign()可以处理数组,但是会把数组视为对象

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

(4)取值函数的处理

Object.assign()只能进行值的复制,如果复制的值是一个取值函数,那么将会去之后在进行复制。

const source = {
  get foo() { return 1 }
};
const target = {};

Object.assign(target, source)
// { foo: 1 }

2.3 常见用途

(1)为对象添加属性

(2)为对象添加方法

(3)克隆对象

function clone(origin) {
  return Object.assign({}, origin);
}

将原始对象拷贝到一个空对象,就得到了原始对象的克隆。

上述方法,只能克隆原始对象自身的值,不能克隆他继承的值。保持继承链,可以采用下述方法

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

(4)合并多个对象

  • 将多个对象合并到某个对象
    const merge =
      (target, ...sources) => Object.assign(target, ...sources);
  • 希望合并后返回一个新对象
    const merge =
      (...sources) => Object.assign({}, ...sources);

(5)为属性指定默认值

3. Object.getOwnPropertyDescriptors()

ES5中的Object.getOwnPropertyDescriptor()方法会返回某个对象属性的描述对象。

ES2017 引入了Object.getOwnPropertyDescriptors()方法,返回指定对象所有自身属性(非继承属性)的描述对象。

const obj = {
  foo: 123,
  get bar() { return 'abc' }
};

Object.getOwnPropertyDescriptors(obj)
// { foo:
//    { value: 123,
//      writable: true,
//      enumerable: true,
//      configurable: true },
//   bar:
//    { get: [Function: get bar],
//      set: undefined,
//      enumerable: true,
//      configurable: true } }

该方法的引入目的,主要是为了解决 Object.assign()无法正确拷贝get属性和set属性的问题。

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

  • __proto__属性用来读取或设置当前对象的原型对象。
  • Object.setPrototypeOf方法的作用与__proto__相同,用来设置一个对象的原型对象(prototype),返回参数对象本身。
  • 该方法与Object.setPrototypeOf方法配套,用于读取一个对象的原型对象。

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

5.1 Object.keys(),

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

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

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

let {keys, values, entries} = Object;
let obj = { a: 1, b: 2, c: 3 };

for (let key of keys(obj)) {
  console.log(key); // 'a', 'b', 'c'
}

for (let value of values(obj)) {
  console.log(value); // 1, 2, 3
}

for (let [key, value] of entries(obj)) {
  console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]
}

5.2 Object.values(),

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

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

上面代码中,属性名为数值的属性,是按照数值大小,从小到大遍历的,因此返回的顺序是b、c、a。

  1. Object.values只返回对象自身的可遍历属性。
    const obj = Object.create({}, {p: {value: 42}});
    Object.values(obj) // []
    上面代码中,Object.create方法的第二个参数添加的对象属性(属性p),如果不显式声明,默认是不可遍历的,因为p的属性描述对象的enumerable默认是falseObject.values不会返回这个属性。

只要把enumerable改成trueObject.values就会返回属性p的值。

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

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

Object.values({ [Symbol()]: 123, foo: 'abc' });   // ['abc']
  1. 如果Object.values方法的参数是一个字符串,会返回各个字符组成的一个数组。
    Object.values('foo')   // ['f', 'o', 'o']

5.3 Object.entries()

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

const obj = { foo: 'bar', baz: 42 };
Object.entries(obj)
// [ ["foo", "bar"], ["baz", 42] ]
  1. 属性名是一个 Symbol 值,该属性会被忽略
    Object.entries({ [Symbol()]: 123, foo: 'abc' });
    // [ [ 'foo', 'abc' ] ]
  2. Object.entries的基本用途是遍历对象的属性。
    let obj = { one: 1, two: 2 };
    for (let [k, v] of Object.entries(obj)) {
      console.log(
        `${JSON.stringify(k)}: ${JSON.stringify(v)}`
      );
    }
    // "one": 1
    // "two": 2
  3. Object.entries方法的另一个用处是,将对象转为真正的Map结构。
    const obj = { foo: 'bar', baz: 42 };
    const map = new Map(Object.entries(obj));
    map // Map { foo: "bar", baz: 42 }

    6. Object.fromEntries()

    Object.fromEntries()方法是Object.entries()的逆操作,用于将一个键值对数组转为对象。
    Object.fromEntries([
      ['foo', 'bar'],
      ['baz', 42]
    ])
    // { foo: "bar", baz: 42 }
  4. 将键值对的数据结构还原为对象,因此特别适合将 Map 结构转为对象。
    // 例一
    const entries = new Map([
      ['foo', 'bar'],
      ['baz', 42]
    ]);
    
    Object.fromEntries(entries)
    // { foo: "bar", baz: 42 }
    
    // 例二
    const map = new Map().set('foo', true).set('bar', false);
    Object.fromEntries(map)
    // { foo: true, bar: false }
  5. 该方法的一个用处是配合URLSearchParams对象,将查询字符串转为对象。
    Object.fromEntries(new URLSearchParams('foo=bar&baz=qux'))
    // { foo: "bar", baz: "qux" }

文章作者: Xin.Zhao
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Xin.Zhao !
评论
  目录