underscore源码学习1--函数库基本构造

172 阅读2分钟

作用域

与一般的工具库如jquery一样,,都是通过一个立即执行函数来包括整块代码内容。如下

(function(){
    // 所有代码
})()

这样做的目的是

  • 引入后立即执行一遍代码
  • 内部定义的变量与方法只存在于该函数的作用域中,不会对外部造成污染

准备工作

关键字初始化

进来先使用了self关键字,在浏览器环境中指向window对象,在node环境中指向global对象

下面一段目的是为了缓存变量,同时减少方法在原型链中的查找次数

构造函数

// Create a safe reference to the Underscore object for use below.
var _ = function(obj) {
    if (obj instanceof _) return obj;
    if (!(this instanceof _)) return new _(obj);
    this.wrapper = obj;
}

大家都知道,underscore的使用方式一般有两种,如下

  • 面向函数
    _.map([1, 2, 3], function(item) {
        item = item + 1;
    });
    _.clone(a, b);
    
  • 面向对象
    _([1, 2, 3]).map();
    _(a).clone(b)
    

声明了一个_函数对象后,后面所有的方法集合都会直接挂到该对象上。使用第一种函数式调用,我们根本不会进入到上面的构造函数中。只有使用面向对象方式调用时才会进入。 我们来看看_([1, 2, 3])执行时发生了什么

  1. 判断obj instanceof _, 此时obj[1, 2, 3], 判断为false,进入下一步
  2. this instanceof _, this指向window,判断为false, 取反,符合条件执行return new _(obj)
  3. new _(obj)再次进入该函数,此时this指向_, 取反后继续执行this.wrapped = obj
  4. 于是最后return一个{wrapper: [1, 2, 3]}对象,对象原型指向_.prototype

输出

一个库最后总是要暴露出来使用的,在没有模块化的年代我们直接使用window._ = _就可以完成了,为了支持模块化,underscore做了很多判断操作,如下

exports._ = _是nodejs模块早期的API exports = module.exports = _是后续nodejs更新后的API写法,其中exportsmodule.exports的一个引用,为使两者输出保持一致采用这种写法

总结

完整伪代码如下

(function() {
// 准备工作
    var root = typeof self == 'object' && self.self === self && self ||
            typeof global == 'object' && global.global === global && global ||
            this ||
            {};
    var previousUnderscore = root._;
    ...
    ...
    
// 构造函数
     var _ = function(obj) {
        if (obj instanceof _) return obj;
        if (!(this instanceof _)) return new _(obj);
        this._wrapped = obj;
      };
      
    ...
    ...
    ...

// 输出
      if (typeof exports != 'undefined' && !exports.nodeType) {
        if (typeof module != 'undefined' && !module.nodeType && module.exports) {
          exports = module.exports = _;
        }
        exports._ = _;
      } else {
        root._ = _;
      }
    
      // Current version.
      _.VERSION = '1.9.1';
    
}())

至此一个工具库的雏形就已经完毕了后续需要在其中挂上各种各样的工具函数即可。

在写具体的工具方法之前,我们还需了解undescore中两个非常的关键方法cboptimizeCb, 我将在下篇文章中为大家详细介绍