杨二

Machine repeats, Human creates

探索Object.assign()

extend obj

起因

最近在重构以前写过的javascript代码,目的是为了去掉对jQuery的依赖。其中有多处用到了$.extend()方法,自然而然会想到这么一个问题:如何用自己的方式来实现?

思路

首先,定义函数的功能:**传入多个对象,将多个对象的内容合并到第一个对象中,最后返回合并后的对象,即参数中的第一个对象。**这样的定义是否似曾相识?没错,和 ES6 中的新特性Object.assign()几乎一模一样。所以,可以从探索Object.assign()开始。

#探索 Object.assign()

Object.assign(target, ...sources)

使用场景

拷贝对象

只需要传入的第一个参数是空对象即可:

var obj = { foo: 1 };
var copy = Object.assign({}, obj);
console.log(copy); // { foo: 1 }

合并对象

注意第一个传参将会被改变,所以如果不想改变第一个有值参数,可以传入一个空对象:

var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };
var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1); // { a: 1, b: 2, c: 3 }, 注意第一个参数,即o1有了变化。

注意事项

  • Object.assign()中,原型属性和非可枚举属性不会被拷贝。(两种属性的解释

    var obj = Object.create(
      { foo: 1 },
      {
        // foo是obj原型链上的属性
        bar: {
          value: 2 // bar 是非可枚举(non-enumerable)属性.
        },
        baz: {
          value: 3,
          enumerable: true // baz是可枚举属性.
        }
      }
    );
    var copy = Object.assign({}, obj);
    console.log(copy); // { baz: 3 },只拷贝了baz属性
  • 元类型会被转化为对象

    var v1 = "abc";
    var v2 = true;
    var v3 = 10;
    var obj = Object.assign({}, v1, null, v2, undefined, v3);
    //元类型里面的字符串被转化为字符数组,null和undefined被忽略
    console.log(obj); // { "0": "a", "1": "b", "2": "c" }
  • 异常会抛出,但是已经执行的代码会生效

    var target = Object.defineProperty({}, "foo", {
      value: 1,
      writable: false
    }); // target.foo 为只读属性
    Object.assign(target, { bar: 2 }, { foo2: 3, foo: 3, foo3: 3 }, { baz: 4 });
    // 会抛出异常:TypeError: "foo" is read-only,但是之前的赋值操作已经生效。
    console.log(target.bar); // 2
    console.log(target.foo2); // 3
    console.log(target.foo); // 1, 异常就是在这儿发生的,所以还是1
    console.log(target.foo3); // undefined
    console.log(target.baz); // undefined

    #pollyfill

上面是对Object.assign()概念层面的探索,由于要考虑旧版本浏览器兼容性问题,需要写一个pollyfill,其实这才是重头戏。不过,只有理解了Object.assign()都干了些什么,才有可能写出来:

(function() {
  if (typeof Object.assign != "function") {
    Object.assign = function(target) {
      //第一个传参不能是undefined和null,因为它们不能被转为对象
      if (target === undefined || target === null) {
        throw new TypeError("Can not convert undefined or null to object");
      }
      //使用Object对象化target
      var output = Object(target);
      for (var idx = 1, l = arguments.length; index < l; idx++) {
        var source = arguments[idx];
        //后续传参也需要判断undefined和null
        if (source !== undefined && source !== null) {
          for (var key in source) {
            if (Object.prototype.hasOwnProperty.call(source, key)) {
              output[key] = source[key];
            }
          }
        }
      }
      return output;
    };
  }
})();

结束

好了,现在可以在宿主环境大胆使用Object.assign()了,Let’s rock it~~~

参考链接:

MDN:Object.assign()