Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

underscore对象浅拷贝核心 #5

Open
xlshen opened this issue Mar 18, 2018 · 0 comments
Open

underscore对象浅拷贝核心 #5

xlshen opened this issue Mar 18, 2018 · 0 comments
Assignees
Labels
enhancement New feature or request

Comments

@xlshen
Copy link
Owner

xlshen commented Mar 18, 2018

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方法进行对象的浅拷贝扩展

@xlshen xlshen self-assigned this Mar 18, 2018
@xlshen xlshen added the enhancement New feature or request label Mar 18, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant