underscore.js源码解析【'_'对象定义及内部函数】
(function() { // Baseline setup // -------------- // Establish the root object, `window` (`self`) in the browser, `global` // on the server, or `this` in some virtual machines. We use `self` // instead of `window` for `WebWorker` support. /* 判断root是window/global/this */ var root = typeof self == 'object' && self.self === self && self || typeof global == 'object' && global.global === global && global || this; // Save the previous value of the `_` variable. /* 保存全局下之前的'_'变量 若变量冲突,则可以利用此变量进行恢复 */ var previousUnderscore = root._; // Save bytes in the minified (but not gzipped) version: /* 原型赋值,以便压缩 */ var ArrayProto = Array.prototype, ObjProto = Object.prototype; var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null; // Create quick reference variables for speed access to core prototypes. /* 将内置对象原型中的常用方法赋值给引用变量,以便更方便的引用 */ var push = ArrayProto.push, slice = ArrayProto.slice, toString = ObjProto.toString, hasOwnProperty = ObjProto.hasOwnProperty; // All **ECMAScript 5** native function implementations that we hope to use // are declared here. /* 定义了一些ECMAScript 5方法 */ var nativeIsArray = Array.isArray, nativeKeys = Object.keys, nativeCreate = Object.create; // Naked function reference for surrogate-prototype-swapping. /* 定义一个裸函数 */ var Ctor = function(){}; // Create a safe reference to the Underscore object for use below. /* 创建'_'函数(对象) */ var _ = function(obj) { /* 如果'_'的prototype在obj的原型链上,直接返回obj */ if (obj instanceof _) return obj; /* 如果不在,new一个'_'对象 */ if (!(this instanceof _)) return new _(obj); /* 如果都不是,则将obj的引用放在_.wrapped属性中???????????????????????????????????????????????????????????????? */ this._wrapped = obj; }; // Export the Underscore object for **Node.js**, with // backwards-compatibility for their old module API. If we're in // the browser, add `_` as a global object. // (`nodeType` is checked to ensure that `module` // and `exports` are not HTML elements.) /* 判断宿主 */ if (typeof exports != 'undefined' && !exports.nodeType) { if (typeof module != 'undefined' && !module.nodeType && module.exports) {//node exports = module.exports = _; } exports._ = _; } else {//浏览器,放在window的属性下 root._ = _; } // Current version. _.VERSION = '1.8.3'; // Internal function that returns an efficient (for current engines) version // of the passed-in callback, to be repeatedly applied in other Underscore // functions. /* 用于内部使用的高阶函数,传入回调函数 主要用来执行函数并改变所执行函数的作用域,最后加了一个argCount参数来指定参数个数 */ var optimizeCb = function(func, context, argCount) { if (context === void 0) return func; /* 对参数个数小于等于4的情况进行分类处理 */ switch (argCount == null ? 3 : argCount) { case 1: return function(value) { return func.call(context, value); }; // The 2-parameter case has been omitted only because no current consumers // made use of it. 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); }; } /* 当argCount不为null或1/3/4时,直接调用func.apply(context, arguments) */ return function() { return func.apply(context, arguments); }; }; var builtinIteratee; // An internal function to generate callbacks that can be applied to each // element in a collection, returning the desired result — either `identity`, // an arbitrary callback, a property matcher, or a property accessor. /* 判断参数类型 */ var cb = function(value, context, argCount) { if (_.iteratee !== builtinIteratee) return _.iteratee(value, context); if (value == null) return _.identity; if (_.isFunction(value)) return optimizeCb(value, context, argCount);// 参数是函数,返回optimizeCb函数的执行结果 if (_.isObject(value)) return _.matcher(value);// 参数是对象,则返回一个能判断对象是否相等的函数 return _.property(value);// 默认返回一个获取对象属性的函数 }; // External wrapper for our callback generator. Users may customize // `_.iteratee` if they want additional predicate/iteratee shorthand styles. // This abstraction hides the internal-only argCount argument. _.iteratee = builtinIteratee = function(value, context) { return cb(value, context, Infinity); }; // Similar to ES6's rest param (http://ariya.ofilabs.com/2013/03/es6-and-rest-parameter.html) // This accumulates the arguments passed into an array, after a given index. /* 类似于es6中的rest param(function f(a, b, ...args) {}) 把startIndex后面的参数放到一个数组里,并作为参数传给fn */ var restArgs = function(func, startIndex) { startIndex = startIndex == null ? func.length - 1 : +startIndex;//'+'相当于Number() return function() { var length = Math.max(arguments.length - startIndex, 0), rest = Array(length), index = 0; for (; index < length; index++) { rest[index] = arguments[index + startIndex]; } switch (startIndex) { case 0: return func.call(this, rest); case 1: return func.call(this, arguments[0], rest); case 2: return func.call(this, arguments[0], arguments[1], rest); } /* 将startIndex之前的参数放进数组中 */ var args = Array(startIndex + 1); for (index = 0; index < startIndex; index++) { args[index] = arguments[index]; } args[startIndex] = rest; return func.apply(this, args); }; }; // An internal function for creating a new object that inherits from another. /* 创建一个继承自另一个对象的新对象(原型继承) */ var baseCreate = function(prototype) { if (!_.isObject(prototype)) return {}; if (nativeCreate) return nativeCreate(prototype);//如果存在Object.create,则直接使用 Ctor.prototype = prototype; var result = new Ctor; Ctor.prototype = null; return result; }; /* 获取对象及其原型链上的方法或属性 */ var property = function(key) { return function(obj) { return obj == null ? void 0 : obj[key]; }; }; // Helper for collection methods to determine whether a collection // should be iterated as an array or as an object. // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength // Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094 /* 判断类数组类型(有length属性,并且0 < length < MAX_ARRAY_INDEX) */ var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; var getLength = property('length'); var isArrayLike = function(collection) { var length = getLength(collection); return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX; }; ...... }());
小结:
1.闭包
整个函数在一个闭包中,避免污染全局变量。通过传入this(其实就是window对象)来改变函数的作用域。和jquery的自执行函数其实是异曲同工之妙。这种传入全局变量的方式一方面有利于代码阅读,另一方面方便压缩。
underscore写法:
(function(){ ... }.call(this));
jquery写法:
(function(window, undefined) { ... })(window);
2.格式
var
nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeBind = FuncProto.bind,
nativeCreate = Object.create;
3.高阶函数的使用
var property = function(key) {
return function(obj) {
return obj == null ? void 0 : obj[key];
};
};
参考资料:http://yalishizhude.github.io/2015/09/22/underscore-source/