结构

作用域包裹

与其他第三库一样,underscore 也通过 立即执行函数 来包裹自己的业务逻辑。一般而言,这些库的立即执行函数主要有以下目的:

  • 避免全局污染:所有库的逻辑,库所定义和使用的变量全部被封装到了该函数的作用域中。
  • 隐私保护:但凡在立即执行函数中声明的函数、变量等,除非是自己想暴露,否则绝无可能在外部获得。
(function(){
  // .....执行逻辑
})()

所以,当我们撰写自己的库的时候,也可以考虑在最外层包裹上一个立即执行函数。既不受外部影响,也不给外部舔麻烦。

_ 对象

underscore 有下划线的意思,所以 underscore 通过一个下划线变量 _ 来标识自身,值得注意的是,_ 是一个函数对象,之后,所有的 api 都会被挂载到这个到对象上,如 _.each, _.map 等:

var _ = function (obj) {
  if (obj instanceof _) return obj;
  if (!(this instanceof _)) return new _(obj);
  this._wrapped = obj;
};

那么问题来了, 为什么 _ 会被设计成一个函数对象,而不是普通对象 {} 呢。显然,这样的设计意味着之后可能存在这样的代码片:

var xxx = _(obj);

这样做的目的是什么呢? 我会在之后的 underscore 内容拾遗 / 面向对象风格的支持 再进行解释。

执行环境判断

underscore 既能够服务于浏览器,又能够服务于诸如 nodejs 所搭建的服务端,underscore 对象 _ 将会依托于当前的所处环境,挂载到不同的全局空间当中(浏览器的全局对象是 window,node 的全局对象是 global)。下面代码展示了 underscore 是如何判断自己所处环境的,这个判断逻辑也为我们自己想要撰写前后端通用的库的时候提供了帮助:

var root = typeof self == 'object' && self.self === self && self ||
        typeof global == 'object' && global.global === global && global ||
        this;

window or self

在 underscore 的判断所处环境的代码中,似乎我们没有看到 window 对象的引用,其实,在浏览器环境下,self 保存的就是当前 window 对象的引用。那么相比较于使用 window,使用 self 有什么优势呢?我们看到 MDN 上有这么一句话:

The Window.self read-only property returns the window itself, as a WindowProxy. It can be used with dot notation on a window object (that is, window.self) or standalone (self). The advantage of the standalone notation is that a similar notation exists for non-window contexts, such as in Web Workers.

概括来说,就是 self 还能用于一些不具有窗口的上下文环境中,比如 Web Workers。所以,为了服务于更多场景,underscore 选择了更加通用的 self 对象。

其次,如果处于 node 环境,那么 underscore 的对象 _ 还将被作为模块导出:

if (typeof exports != 'undefined' && !exports.nodeType) {
  if (typeof module != 'undefined' && !module.nodeType && module.exports) {
      exports = module.exports = _;
  }
  exports._ = _;
} else {
  root._ = _;
}

results matching ""

    No results matching ""