ES6对象扩展(二)

471 阅读3分钟

封装对象

  • 阻止对象扩展Object.preventExtensions(),与之对应的一个方法Object.isExtensible(),检测传入的对象是否可扩展,返回一个布尔值,true 可扩展,false不可扩展。

    let obj = {
      a: 1
    }
    
    Object.preventExtensions(obj); // 返回值为传入的obj本身
    
    obj.b = 2;
    console.log(obj); // -> {a: 1}
    
    Object.defineProperty(obj, 'c', {  // -> 直接报错
     value: 3
    })
    console.log(Object.isExtensible(obj)); // -> false 
    
  • 密封对象Object.seal(),内部通过调用Object.preventExtensions(),还有一个循环操作,将遍历到的属性的configurable设置为falseObject.isSealed(),检测对象是否密封, true 为密封,false不密封的。

    let obj = {
      a: 1,
      b: 2
    }
    Object.seal(obj); // 不可配置,不可删除 但是可修改
    
    obj.c = 3;
    console.log(obj); // -> {a: 1, b: 2}
    
    delete obj.a;
    console.log(obj); // -> {a: 1, b: 2}
    
    obj.a = 3;
    console.log(obj); // -> {a: 3, b: 2}
    
    console.log(Object.isSealed(obj)); // -> true
    
  • 冻结对象,Object.freeze(),不可写、不可扩展、不可删除。Object.isFrozen(),检测对象是否冻结,true 冻结,false没冻结。

    let obj = {
      a: 1,
      b: 2,
      c: {
        name: 'foo'
      }
    }
    Object.freeze(obj);
    Object.isFrozen(obj); // -> true
    // 浅拷贝的冻结
    obj.c.name = 'bar';
    console.log(obj); // -> {a: 1, b: 2, c: {name: 'bar'}}
    
    // 封装深拷贝冻结
    function deepFreeze (obj) {
      Object.freeze(obj);
      for(let key in obj){
        if(typeof(obj[key]) === 'object' && obj[key] !== null){
          deepFreeze(obj[key]);
        }
      }
    }
    

is

  • Object.is()=== 内部都是调用sameValue算法,但是有一下两点不同。

    console.log(NaN === NaN); // -> false
    console.log(+0 === -0); // -> ture
    
    console.log(Object.is(NaN, NaN)); // -> true
    console.log(Object.is(+0, -0)); // -> false
    

合并对象

  • Object.assign(tar, ...sources),tar为目标对象,sources原对象可以有多个,返回值为目标对象。

    let o = {
      name: 'foo'
    }
    let t = {};
    
    let copy = Object.assign(t, o);
    console.log(copy === t); // -> true 说明返回值就是第一个参数目标对象
    console.log(t); // -> {name: "foo"}
    
  • 合并/拷贝时,如果出现同名属性,后面的会覆盖前面的。

    let tar = {
      a: 1,
      b: 1,
    }
    let s1 = {
      b: 2,
      c: 2
    }
    let s2 = {
      c: 3
    }
    Object.assign(tar, s1, s2);
    console.log(tar); // -> {a: 1, b: 2, c: 3}
    
  • 当第一个参数不是对象,会报错。特殊的会调用其对应的包装类的方式,转换为对象。当第二个参数不是对象时,默认不做处理,返回第一个目标对象;可以转为对象的,如果转换为对象的属性可枚举,则可以合并,否则不做处理。

    Object.assign(null, {a: 1}); // -> TypeError
    Object.assign(undefined, {a: 1}); // -> TypeError
    
    Object.assign(1, {a: 1}); // -> Number {1, a: 1}
    Object.assign('1', {a: 1}); // -> String {"1", a: 1}
    Object.assign(true, {a: 1}); // -> Boolean {true, a: 1}
    // --------------------------------------------------
    Object.assign({a: 1}, undefined); // -> {a: 1}
    Object.assign({a: 1}, null); // -> {a: 1}
    Object.assign({a: 1}, 1); // -> {a: 1}
    Object.assign({a: 1}, '33'); // -> {0: "3", 1: "3", a: 1}
    Object.assign({a: 1}, true); // -> {a: 1}
    
  • 用于拷贝时, 继承属性和不可枚举属性不能拷贝且属于浅拷贝。

    // Object.create() 创建一个对象,第一个参数为这个对象指定原型,第二个参数是一个对象
    用于配置对象属性
    let obj = Object.create({a: 1}, {
      b: {
        value: 2
      },
      c: {
        value: 3,
        enumerable: true
      }
    })
    console.log(obj); // -> {c: 3, b: 2} 
    
    let copy = Object.assign({}, obj);
    console.log(copy); // -> {c: 3}
    
  • 数组也是特殊的对象,合并时有一个坑。

    let res = Object.assign([1, 2, 3], [4, 5]);
    // 根据数组成员对应的下标合并, 1, 2 和 4, 5 的下标相同 所以发生了替换
    console.log(res); // -> [4, 5, 3]
    

属性描述符s

  • 上一章介绍了单个属性描述符和配置单个属性,defineProperties()getOwnPropertyDescriptors(),分别为配置多个属性和获取所有属性描述。

    let obj = {};
    
    Object.defineProperties(obj, {
      a: {
        value: 1,
        writable: true
      },
      b: {
        value: 2,
        writable: false
      }
    })
    console.log(obj);
    Object.getOwnPropertyDescriptors(obj);
    
    // 可以完美解决 assign 不能拷贝 取值函数和存值函数的问题
    
    let source = {
      get foo () {
        // ...
      },
      set foo (val) {
        // ...
      }
    }
    
    let tar = {};
    Object.assign(tar, source);
    
    Object.defineProperties(tar, Object.getOwnPropertyDescriptors(source));