• 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/

  • 相关阅读:
    QT下载速度慢的解决方法
    第七章 多态
    第六章 重复运用class
    第五章 隐藏实现细节
    代码改变世界
    第四章 初始化和清理
    第三章 控制程序流程
    module.exports和exports
    如何与外部源交互
    实现POST服务器
  • 原文地址:https://www.cnblogs.com/shytong/p/5804306.html
Copyright © 2020-2023  润新知