1625行,解开 underscore.js 的面纱 - 第一章

一向想写一篇如许的文章,因而心动不如行为,这里挑选的是 Underscore.js 1.8.3 版本,源码解释加在一同1625行。

  • Underscore.js 1.8.3

  • http://underscorejs.org

  • (c) 2009-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors Underscore may be freely distributed under the MIT license.

这里我们起首看到的是一个闭包,观点不再熬述,诸君故意详勘闭包的观点,请移步 Closures。源码以下:

(function() {

这里假如这里有 this 那末一定是指向 window,即:

Window {external: Object, chrome: Object, document: document, speechSynthesis: SpeechSynthesis, caches: CacheStorage…}

window 具有的浩瀚属性中就包含了 self 援用其自身,依据javascript的运算符实行递次:

  • . [] () 字段接见、数组下标、函数挪用以及表达式分组

  • ++ — – ~ ! delete new typeof void 一元运算符、返回数据范例、对象建立、未定义值

  • * / % 乘法、除法、取模

  • + – + 加法、减法、字符串衔接

  • << >> >>> 移位

  • < <= > >= instanceof 小于、小于即是、大于、大于即是、instanceof

  • == != === !== 即是、不即是、严厉相称、非严厉相称

  • & 按位与

  • ^ 按位异或

  • | 按位或

  • && 逻辑与

  • || 逻辑或

  • ?: 前提

  • = 赋值、运算赋值

  • , 多重求值

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

这里起首推断的是存在 self 或许 node 环境下的全局变量 global,然后复制给 root,作为根对象。

  var previousUnderscore = root._;

previousUnderscore,从字面上明白就是“之前的 underscore”,说实话我并没明白这个赋值的意图,最最先以为是用来做推断全局 window是不是已存在 window._ 这个对象,然后经由过程推断 previousUnderscore 用来防止 window._ 污染 underscore 引发定名争执,然则从头至尾只要一个处所用到了 previousUnderscore,即(1352行):

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

在外部可实行 var underscore_cache = _.noConflict(); 用来从新定义 underscore 定名,很简朴也很奇妙,noConflict 要领内将 root._ 也就是 window._ 从新定义为 previousUnderscore (previousUnderscore = undefined),而 noConflict 是_的一个属性要领,所以 this 指向其自身(41行),行将 _ 赋值给了 underscore_cache。

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

 var ArrayProto = Array.prototype, ObjProto = Object.prototype;

这两句很简朴,就是将原生 JAVASCRIPT 的 Array 和 Object 对象的 prototype 缓存,如许做的优点是运用 push、slice、toString等要领的代码行数会削减、削减 JAVASCRIPT 遍历等等,更详细的引见会在下面解说,不要心急。

  var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null;

2009年的 ES5 划定了六种言语范例:Null Undefined Number Boolean String Object,详见ES5/范例ES5/范例转换与测试。新出台的 ES6 则划定,包含六种原始范例:Null Undefined Number Boolean String 和 Symbol,另有一种 Object,详见JavaScript 数据范例和数据结构。新增添的 Symbol 很早就已提出,其详细观点这里不再复述请移步参考 Symbol ,得益于 ES6 的逐渐提高,客户端浏览器也有许多已支撑 Symbol,比方 Firefox v36+ 和 Chrome v38+ 等,详细参考 ES6 支撑状况,假如人人对 ES6 想要深切相识能够看 ES6 In Depth 这篇文章和 ES6草案,说实话我的程度有限这份草案还没有读懂(+﹏+),假如想要进一步为 ES6 提高孝敬自身的气力 ES6 WIKI 的编写是一个蛮好的挑选。

回归正题,上述代码的目标不言而喻就是推断客户端是不是支撑 Symbol,支撑则缓存 Symbol.prototype 原型链,不支撑则赋值为 Null,三元运算符的灵活运用是推断一个人言语抵达一个阶段的标识,这句话有点果断,然则算的上肺腑之言,要熟习且灵活运用它。

  var push = ArrayProto.push,
      slice = ArrayProto.slice,
      toString = ObjProto.toString,
      hasOwnProperty = ObjProto.hasOwnProperty;

这里是简朴缓存了 push、slice、toString、hasOwnProperty 四个要领。

  var nativeIsArray = Array.isArray,
      nativeKeys = Object.keys,
      nativeCreate = Object.create;

这里就比较故意思了,Array.isArray(element) 是 ES5 厥后新增的静态函数,用来推断一个对象是不是是数组,详细形貌可见 Array.isArray() 和 Array.isArray 函数 (JavaScript):https://msdn.microsoft.com/zh-cn/library/ff848265(v=vs.94).aspx,我一点都不喜好微软,就比方如今我想粘一个微软的网址,然则它的网址内里居然有(),以致于我必需把网址贴到代码框里才保证不涌现毛病ヽ(ˋДˊ)ノ。Object.keys 用于返回一个由给定对象的一切可罗列自身属性的属性名构成的数组,Object.keys()。Object.create 用于建立一个具有指定原型和若干个指定属性的对象,这一系列的函数要领都能够在 Object 处相识概况。同时这内里有些内容能够参考 Annotated ECMAScript 5.1,有兴致的同砚能够看一看,雾里探花,蛮风趣的。

  var Ctor = function(){};

ctor 英文译为男星,或许我的百度翻译打开方式不对,翻译错了???,实际上就是一个空的要领,这类写法很罕见,平常用于和 call、apply、argument 等合营运用,在 Underscore.js 中作者并没有上述的用法,只是用 Ctor 这个函数扩大了自身的 prototype,将一些函数要领绑定到自身作为一个 return function,详细细节背面接触到再详述。

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

定义 _ 对象,作者的备注是”Create a safe reference to the Underscore object for use below.“,这里我们相识到 _ 自身是一个函数,而在 JAVASCRIPT 中函数自身就是对象的一种,所以 Underscore.js 的一系列函数都是作为对象函数绑定到 _ 这个函数对象上面的,上面这个函数默许传入一个 obj 参数,能够经由过程 _(obj) 用来校验 _ 是不是是 obj 的父范例以此推断继续关联,instanceof的用法详见 JavaScript instanceof 运算符深切理会,至于 _wrapped 涉及到背面的链式操纵,在(887行)一同讲。

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

这是 Node.js 中对通用模块的封装要领,经由过程对推断 exports 是不是存在来决定将局部变量 _ 赋值给exports,趁便说一下 AMD 范例、CMD范例和 UMD范例,Underscore.js 是支撑 AMD 的,在源码尾部有定义,这里简朴叙说一下:

amd:AMDJS

define(['underscore'], function (_) {
    //todo
});

cmd:Common Module Definition / draftCMD 模块定义范例

var _ = require('underscore');
module.exports = _;

另一种罕见的写法:

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define(['underscore'], factory);
    } else if (typeof exports === 'object') {
        module.exports = factory(require('underscore'));
    } else {
        root.returnExports = factory(root._);
    }
}(this, function ($) {
    //todo
}));
  _.VERSION = '1.8.3';

underscore 版本为 ‘1.8.3’。

  var optimizeCb = function(func, context, argCount) {
    if (context === void 0) return func;
    switch (argCount == null ? 3 : argCount) {
      case 1: return function(value) {
        return func.call(context, value);
      };
      case 3: return function(value, index, collection) {
        return func.call(context, value, index, collection);
      };
      case 4: return function(accumulator, value, index, collection) {
        return func.call(context, accumulator, value, index, collection);
      };
    }
    return function() {
      return func.apply(context, arguments);
    };
  };

optimizeCb 翻译成汉语就是优化回调(optimize callback),那末 optimizeCb 是怎样优化的呢,我们能够起首看到它传入了三个参数,分别为:func、context、argCount,语义化可知一个是将要优化的 callback function,一个是 context 上下文函数,末了 argCount 是一个 number 范例的数字。void 0 的用法很奇妙,这里用 context === void 0 推断是不是存在上下文环境,也就是第二个参数,其他的一些关于 void 的用法详见 谈谈Javascript中的void操纵符。接下来推断 argCount 数字举行响应的操纵,其中有 call 和 apply 两个要领,详见 Function.prototype.apply()Function.prototype.call()

    原文作者:kangkk
    原文地址: https://segmentfault.com/a/1190000005639920
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞