underscore对象浅拷贝核心

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容