美文网首页underscore.js
underscore对象浅拷贝核心

underscore对象浅拷贝核心

作者: 13日星期五 | 来源:发表于2018-03-21 10:00 被阅读0次

    underscore对象浅拷贝核心

    核心函数:createAssigner()

    源码:

      // An internal function for creating assigner functions.
      // 浅拷贝代码核心
      // 内部调用函数:_.extend(), _.extendOwn(), _.defaults()
      // 1. _.extend = createAssigner(_.allKeys);
      // 2. _.extendOwn = _.assign = createAssigner(_.keys);
      // 3. _.defaults = createAssigner(_.allKeys, true);
      // 文章号:[underscore对象浅拷贝核心](https://github.com/xlshen/underscore/issues/5)
      // keysFunc为_.key或者_.allKeys方法,返回对象的属性名组成的数组
      var createAssigner = function(keysFunc, defaults) {
          // 返回函数闭包,通过传入的对象执行以下函数
          return function(obj) {
              // 获取传入执行函数参数个数
              var length = arguments.length;
              // 如果defaults执行true,再对传入对象包装一下,此时如果obj === null || undefined
              // 则obj也转换为对象,则执行下一步时就不会因为obj为null而返回null
              if (defaults) obj = Object(obj);
              // 如果参数只有原始对象一个或者没有的情况,或者obj值为null的话,直接返回该对象
              // 1. _.extend()和_.extendOwn()调用是defaults为undefined,此时length<2或者obj值为null时,返回该对象
              // 2. _.defaults()调用时,defaults为true,此时length<2时,返回该对象【注:此时obj不可能为null,因为即使原始传入的obj为null,经过上一步操作,null也会转换为Object对象】
              if (length < 2 || obj == null) return obj;
              // _.extendOwn(destination, source1, source2, ...) 外层循环从source1开始取值
              // 外层循环参数取源对象source1,source2...,从source1开始,顺序往后执行
              for (var index = 1; index < length; index++) {
                      // 获取源对象
                  var source = arguments[index],
                      // 返回源对象中的属性名数组
                      keys = keysFunc(source),
                      // 源对象属性名数组长度
                      l = keys.length;
                  // 内层循环源对象属性名数组
                  for (var i = 0; i < l; i++) {
                      // 获取属性名
                      var key = keys[i];
                      // 1. _.extendOwn(), _.extend()执行时defaults === false,直接覆盖目标对象相应的属性值
                      // 2. _.defaults()执行时defaults === true且目标对象当前属性为undefined时,赋值该对象该属性值;如果该对象该属性存在,不执行任何操作
                      if (!defaults || obj[key] === void 0) obj[key] = source[key];
                  }
              }
              // 返回目标对象
              return obj;
          };
      };
    

    内部调用函数:_.extend() || _.extendOwn() || _.defaults()

      // Fill in a given object with default properties.
      // 扩展目标对象,但是禁止扩展修改目标对象已经存在的属性键值对
      _.defaults = createAssigner(_.allKeys, true);
      // Extend a given object with all the properties in passed-in object(s).
      // 扩展目标对象,对于源对象及其原型链中所有可枚举属性对目标对象进行扩展修改
      _.extend = createAssigner(_.allKeys);
    
      // Assigns a given object with all the own properties in the passed-in object(s).
      // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
      // 扩展目标对象,对于源对象仅获取自身可枚举属性扩展修改目标对象,但是源对象原型链属性不进行扩展修改
      _.extendOwn = _.assign = createAssigner(_.keys);
    

    实例:

      var _proto = {name: "xlshen", userName: "13日星期五"};
      var obj = Object.create(_proto, {
        age: {
          value: 20,
          writable: true,
          enumerable: true,
          configurable: true
        }
      });
      _.extend({age: 18}, obj); // {name: "xlshen", age: 20, userName: "13日星期五"}
      _.extendOwn({name: "xlshen"}, {name: "XXX", age: 20}); // {name: "XXX", age: 20}
      _.defaults({name: "xlshen"}, {name: "XXX", age: 20}); // {name: "xlshen", age: 20}
    

    解析:

    • _.extend执行中keysFunc_.allKeys方法,返回的是对象本身及其原型链上所有可枚举属性名组成的数组;执行createAssigner方法,在上例中目标对象为{age: 18}arguments.length2defaultsundefined;继续往下执行,length == 2 && obj != null;继续往下执行,开始依次循环源对象,因源对象从第二个参数开始,所以index取值从1开始,在循环内部,获取源对象source,执行keysFunc方法获取source自身和原型上所有属性组成的数组,得到属性个数l;依次循环属性数组,!defaults始终为true,始终执行obj[key]=source[key]向目标对象赋值操作

    • _.extendOwn_.extend方法唯一不同的就是keysFunc函数,在_.extendOwn中,keysFunc_.keys方法,返回的是对象自身所有可枚举属性组成的数组,剩下的都和_.extend方法一样

    • _.defaults执行和上面两个都略有区别:在_.defaultscreateAssigner函数中keysFunc函数为_.allKeys方法,defaults置为true;执行createAssigner方法,第一步获取传入对象个数;因为defaults始终为true,第二步执行Object包装,此处可以兼容如果传入obj为空或者null的情况;如果参数个数<2则直接返回目标对象【注:此时obj不可能为null,因为即使原始传入的objnull,经过上一步操作,null也会转换为Object对象】;往下和_.extend方法一样,依次执行外层循环、内层循环,在内层循环的最后,因为!defaults始终为false,此处就是判断,如果obj[key] === void 0则赋值,否则如果该属性已经存在目标对象中,则不进行任何操作

    _.extend || _.extendOwn || _.defaults方法通过调用createAssigner方法进行对象的浅拷贝扩展

    相关文章

      网友评论

        本文标题:underscore对象浅拷贝核心

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