美文网首页
(七)对象的扩展

(七)对象的扩展

作者: 做最棒的 | 来源:发表于2018-08-29 15:37 被阅读0次

    1、函数参数的默认值

    ES6 允许直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。

    const foo = 'bar';
    const baz = {foo};
    baz // {foo: "bar"}
    
    // 等同于
    const baz = {foo: foo};
    let birth = '2000/01/01';
    const Person = {
      name: '张三',
      //等同于birth: birth
      birth,
      // 等同于hello: function ()...
      hello() { console.log('我的名字是', this.name); }
    };
    

    2、属性名表达式

    在高程中已经分享过,一起回顾

    // 方法一
    obj.foo = true;
    
    // 方法二
    obj['a' + 'bc'] = 123;
    
    let propKey = 'foo';
    
    let obj = {
      [propKey]: true,
      ['a' + 'bc']: 123
    };
    
    let lastWord = 'last word';
    
    const a = {
      'first word': 'hello',
      [lastWord]: 'world'
    };
    
    a['first word'] // "hello"
    a[lastWord] // "world"
    a['last word'] // "world"
    
    let obj = {
      ['h' + 'ello']() {
        return 'hi';
      }
    };
    
    obj.hello() // hi
    

    错误用法

    const keyA = {a: 1};
    const keyB = {b: 2};
    
    const myObject = {
      [keyA]: 'valueA',
      [keyB]: 'valueB'
    };
    
    myObject // Object {[object Object]: "valueB"}
    

    3、方法的 name 属性

    函数的name属性,返回函数名。对象方法也是函数,因此也有name属性。

    const person = {
      sayName() {
        console.log('hello!');
      },
    };
    
    person.sayName.name   // "sayName"
    

    其它 篇幅自己看

    4、Object.is()

    参考我分享的ES5基础

    5、Object.assign()

    完全按照阮一峰es6讲,写的非常全面

    基本用法

    const target = { a: 1 };
    
    const source1 = { b: 2 };
    const source2 = { c: 3 };
    
    Object.assign(target, source1, source2);
    target // {a:1, b:2, c:3}
    

    Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)

    5.1) 源对象(source)可以有无限多个,目标对象(target)只能有一个
    5.2) undefined和null无法转成对象,所以如果它们作为参数,就会报错
    Object.assign(undefined) // 报错
    Object.assign(null) // 报错
    
    5.3) 如果该参数不是对象,则会先转成对象,然后返回。
    console.log(typeof Object.assign(2));
    console.log(Object('abc'))
    console.log(Object(10))
    console.log(Object(true))
    const abc = Object.assign(2, {ID:1},{name:3});
    console.log(abc)
    
    5.4) Object.assign拷贝的属性是有限制的,只拷贝源对象的自身属性(不拷贝继承属性),也不拷贝不可枚举的属性(enumerable: false)。
    5.5) Object.assign替换方法 ...运算符
    {...obj}
    {...obj,...obj2}
    

    注意点

    1) 浅拷贝

    Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。

    2) 同名属性的替换

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

    const target = { a: { b: 'c', d: 'e' } }
    const source = { a: { b: 'hello' } }
    Object.assign(target, source)
    // { a: { b: 'hello' } }
    
    3) 数组的处理

    Object.assign可以用来处理数组,但是会把数组视为对象。

    Object.assign([1, 2, 3], [4, 5])
    // [4, 5, 3]
    
    4) 取值函数的处理

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

    const source = {
      get foo() { return 1 },
      get fhh() { return 2 }
    };
    const target = {};
    console.log(Object.assign(target, source));
    console.log(source);
    

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

    常见用途

    1)为对象添加属性
    class Point {
      constructor(x, y) {
        Object.assign(this, {x, y});
      }
    }
    // 看输出效果
    const point = new Point();
    console.log(point);
    

    上面方法通过Object.assign方法,将x属性和y属性添加到Point类的对象实例

    2)为对象添加方法
    Object.assign(SomeClass.prototype, {
      someMethod(arg1, arg2) {
        ···
      },
      anotherMethod() {
        ···
      }
    });
    
    // 等同于下面的写法
    SomeClass.prototype.someMethod = function (arg1, arg2) {
      ···
    };
    SomeClass.prototype.anotherMethod = function () {
      ···
    };
    

    上面代码使用了对象属性的简洁表示法,直接将两个函数放在大括号中,再使用assign方法添加到SomeClass.prototype之中。

    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);
    

    推荐

    {...sources}
    
    5)为属性指定默认值

    自己看

    可枚举性

    let obj = { foo: 123 };
    console.log(Object.getOwnPropertyDescriptor(obj, 'foo'));
    

    实例

    var o = {a:1, b:2};
    
    o.c = 3;
    Object.defineProperty(o, 'd', {
      value: 4,
      enumerable: false
    });
    
    o.d
    // 4
    
    for( var key in o ) console.log( o[key] ); 
    // 1
    // 2
    // 3
    console.log(Object.keys(o));// ["a", "b", "c"]
    console.log(JSON.stringify(o));   // => "{a:1,b:2,c:3}"
    

    有四个操作会忽略enumerable为false的属性。

    for...in循环:只遍历对象自身的和继承的可枚举的属性。
    Object.keys():返回对象自身的所有可枚举的属性的键名。
    JSON.stringify():只串行化对象自身的可枚举的属性。
    Object.assign(): 忽略enumerable为false的属性,只拷贝对象自身的可枚举的属性。

    属性的遍历
    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 键,按照加入时间升序排列。
    Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0 })
    // ['2', '10', 'b', 'a', Symbol()]
    上面代码中,Reflect.ownKeys方法返回一个数组,包含了参数对象的所有属性。这个数组的属性次序是这样的,首先是数值属性2和10,其次是字符串属性b和a,最后是 Symbol 属性。

    尽量不要用for...in循环,而用Object.keys()代替

    6、Object.keys(),Object.values(),Object.entries()

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

    ES2017 [引入] 了跟Object.keys配套的Object.valuesObject.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]
    }
    
    let obj = { one: 1, two: 2 };
    for (let [k, v] of Object.entries(obj)) {
      console.log(
        `${JSON.stringify(k)}: ${JSON.stringify(v)}`
      );
    }
    
    

    实战

    var data = {a:1, b:2};
    const arr = [];
    Object.entries(data).forEach(([key, value]) => {
      if (typeof value !== 'undefined' && value != null) {
            arr.push(key + '=' + encodeURIComponent(value));
        }
    });
    console.log(arr.join('&'));
    // 为什么是这个样子,[key, value]
    console.log(Object.entries(data));
    //注意key,value的解构赋值
    
    
    7、Object.getOwnPropertyDescriptor

    Object.getOwnPropertyDescriptor方法会返回某个对象属性的描述对象(descriptor)。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属性的问题

    const source = {
      set foo(value) {
        console.log(value);
      }
    };
    
    const target1 = {};
    Object.assign(target1, source);
    
    Object.getOwnPropertyDescriptor(target1, 'foo')
    // { value: undefined,
    //   writable: true,
    //   enumerable: true,
    //   configurable: true }
    

    Object.getOwnPropertyDescriptors方法配合Object.defineProperties方法,就可以实现正确拷贝。

    const source = {
      set foo(value) {
        console.log(value);
      }
    };
    
    const target2 = {};
    Object.defineProperties(target2, Object.getOwnPropertyDescriptors(source));
    Object.getOwnPropertyDescriptor(target2, 'foo')
    // { get: undefined,
    //   set: [Function: set foo],
    //   enumerable: true,
    //   configurable: true }
    

    上面代码中,两个对象合并的逻辑可以写成一个函数。

    const shallowMerge = (target, source) => Object.defineProperties(
      target,
      Object.getOwnPropertyDescriptors(source)
    );
    

    bject.getOwnPropertyDescriptors方法的另一个用处,是配合Object.create方法,将对象属性克隆到一个新对象。这属于浅拷贝

    const clone = Object.create(Object.getPrototypeOf(obj),
      Object.getOwnPropertyDescriptors(obj));
    
    // 或者
    
    const shallowClone = (obj) => Object.create(
      Object.getPrototypeOf(obj),
      Object.getOwnPropertyDescriptors(obj)
    );
    
    vue里面怎么实现双向绑定?

    Object.getOwnPropertyDescriptors()
    Object.getOwnPropertyDescriptor()
    Object.defineProperties()
    Object.defineProperty()
    扩展
    https://segmentfault.com/a/1190000011294519#articleHeader18
    面向对象扩展
    https://segmentfault.com/a/1190000015377875

    其它Object其它方法自己看
    找机会单独分享,小伙伴也可以毛遂自荐
    [JavaScript——原型:prototype、constructor、proto]
    Object.getPrototypeOf()
    Object.setPrototypeOf()
    Object.create()
    Object.getOwnPropertySymbols
    super 关键字
    (https://segmentfault.com/a/1190000012446557)

    相关文章

      网友评论

          本文标题:(七)对象的扩展

          本文链接:https://www.haomeiwen.com/subject/tvckwftx.html