js高手进阶之路:underscore源码经典(三)

后面的代码真是越看越难理解,经常需要结合内部/接口函数一起,所以采用跳读的方式解析,基本按照模块由易到难的顺序解析。

456  _.initial = function(array, n, guard) {
    return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
  };

这个guard参数说是保证默认返回第一个值,不过我看了很久觉得意义不大,猜测可能是以前的接口。这里的slice = Array.prototype.slice。这里有两个地方比较好,一是用Array的原型函数来处理array参数,这样可以避免判断array是否数组,二是用max来避免超出数组长度。

671 _.range = function(start, stop, step) {
    if (arguments.length <= 1) {
      stop = start || 0;
      start = 0;
    }
    step = step || 1;

    var length = Math.max(Math.ceil((stop - start) / step), 0);
    var range = Array(length);

    for (var idx = 0; idx < length; idx++, start += step) {
      range[idx] = start;
    }

    return range;
  };

这个函数本什没有什么特别的,只不过我在调用的时候将step赋值为小数,就出现了javascript著名的精度问题。网上应该有不少的解决方案,这里提供一种目前我在项目中用到的解决方法:将数字转为字符串在去小数点之后转为整数进行计算,计算完成后转字符串加上小数点再转成小数,举个例子

1.11 ==> "1.11" ==> "111" ==> 111
2.2 ==> "2.2" ==> "220" ==> 220
111+220 ==> 331 ==> "331" ==> "3.31" ==> 3.31

数组代码看得累了,从后面的看起吧~

1269 _.noConflict = function() {
  root._ = previousUnderscore;
  return this;
};

这个让渡函数其实原理比较简单,就是初始化underscore前 previousUnderscore=window._对原有对象进行储存,如果需要让渡避免冲突,则进行还原同时返回当前underscore对象。再来看看JQuery的实现原理:

noConflict: function( deep ) {
  if ( window.$ === jQuery ) {
    window.$ = _$;
  }

  if ( deep && window.jQuery === jQuery ) {
    window.jQuery = _jQuery;
  }

  return jQuery;
}

刚开始看的时候犯2了,函数可以用===来比较?不是只能比较简单的数据对象?呵呵,其实这个是通过引用地址来判断的,因为之前初始化的时候window.$=JQuery所以$会指向JQuery的地址,所以可以这样判断。后面一个if考虑到JQuery这个变量名也被占用的时候,也进行让渡,最后返回JQuery对象。可能是考虑多版本的情况吧~

1487  _.mixin = function(obj) {
  _.each(_.functions(obj), function(name) {
    var func = _[name] = obj[name];
    _.prototype[name] = function() {
      var args = [this._wrapped];
      push.apply(args, arguments);
      return result(this, func.apply(_, args));
    };
  });
};

// Add all of the Underscore functions to the wrapper object.
_.mixin(_);

这个用于扩展underscore自身的接口函数,通过循环遍历对象来浅拷贝对象属性。这里考虑到函数的实例化,所以一方面将函数作为内部函数使用,同时扩展到prototype属性上。考虑得比较周全。

1469  _.chain = function(obj) {
  var instance = _(obj);
  instance._chain = true;
  return instance;
};

这个函数让underscore支持链式调用,链式调用写起来很美观,但是调试略显不方便。结合之前的代码来看,underscore实现链式调用的基本原理是:将参数缓存在内部属性_wrapped中,调用函数时传入该属性值,执行后返回当前this指针。直到执行value函数时返回_wraped中的值。


一部由众多技术专家推荐, 帮你成为具有全面能力和全局视野工程师的进阶利器—— 《了不起的JavaScript工程师》出版了! 点击下方链接即刻踏上进阶之路!


亚里士朱德 wechat
更多WEB技术分享请订阅微信公众号“WEB学习社”