underscore.js源码解析函数

Posted 很好玩的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了underscore.js源码解析函数相关的知识,希望对你有一定的参考价值。

  // Function (ahem) Functions
  // ------------------

  // Determines whether to execute a function as a constructor
  // or a normal function with the provided arguments.
  /*
    判断一个函数是按照构造函数还是普通函数执行??????????????????????????????????????????????????????????
  */
  var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {
    if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
    var self = baseCreate(sourceFunc.prototype);// 原型继承sourceFourceFunc.prototype,返回一个空对象
    var result = sourceFunc.apply(self, args);// 
    if (_.isObject(result)) return result;
    return self;
  };

  // Create a function bound to a given object (assigning `this`, and arguments,
  // optionally). Delegates to **ECMAScript 5**‘s native `Function.bind` if
  // available.
  /*
    绑定函数 function 到对象 object 上, 也就是无论何时调用函数, 函数里的 this 都指向这个 object.任意可选参数 arguments 可以传递给函数 function , 可以填充函数所需要的参数,这也被称为 partial application。
    (愚人码头注:partial application翻译成“部分应用”或者“偏函数应用”。partial application可以被描述为一个函数,它接受一定数目的参数,绑定值到一个或多个这些参数,并返回一个新的函数,这个返回函数只接受剩余未绑定值的参数。参见:http://en.wikipedia.org/wiki/Partial_application。感谢@一任风月忆秋年的建议)。
  */
  _.bind = restArgs(function(func, context, args) {
    if (!_.isFunction(func)) throw new TypeError(‘Bind must be called on a function‘);
    var bound = restArgs(function(callArgs) {
      return executeBound(func, bound, context, this, args.concat(callArgs));
    });
    return bound;
  });

  // Partially apply a function by creating a version that has had some of its
  // arguments pre-filled, without changing its dynamic `this` context. _ acts
  // as a placeholder by default, allowing any combination of arguments to be
  // pre-filled. Set `_.partial.placeholder` for a custom placeholder argument.
  /*
    局部应用一个函数填充在任意个数的 arguments,不改变其动态this值。和bind方法很相近。你可以传递_ 给arguments列表来指定一个不预先填充,但在调用时提供的参数。
  */
  _.partial = restArgs(function(func, boundArgs) {
    var placeholder = _.partial.placeholder;// 不预先填充
    var bound = function() {
      var position = 0, length = boundArgs.length;// position表示实参中的位置
      var args = Array(length);
      for (var i = 0; i < length; i++) {// 把之前的boundArgs和后传进来的实参拼起来
        args[i] = boundArgs[i] === placeholder ? arguments[position++] : boundArgs[i];// 如果参数为_,则取实参
      }
      while (position < arguments.length) args.push(arguments[position++]);
      return executeBound(func, bound, this, this, args);// 执行func
    };
    return bound;
  });

  _.partial.placeholder = _;

  // Bind a number of an object‘s methods to that object. Remaining arguments
  // are the method names to be bound. Useful for ensuring that all callbacks
  // defined on an object belong to it.
  /*
    把methodNames参数指定的一些方法绑定到object上,这些方法就会在对象的上下文环境中执行。绑定函数用作事件处理函数时非常便利,否则函数被调用时this一点用也没有。methodNames参数是必须的。
  */
  _.bindAll = restArgs(function(obj, keys) {
    keys = flatten(keys, false, false);// 打开嵌套数组,返回新数组
    var index = keys.length;
    if (index < 1) throw new Error(‘bindAll must be passed function names‘);
    while (index--) {
      var key = keys[index];
      obj[key] = _.bind(obj[key], obj);// 绑定obj[key]到obj中
    }
  });

  // Memoize an expensive function by storing its results.
  /*
    Memoizes方法可以缓存某函数的计算结果。对于耗时较长的计算是很有帮助的。如果传递了 hasher 参数,就用 hasher 的返回值作为key存储函数的计算结果。hashFunction 默认使用function的第一个参数作为key。memoized值的缓存可作为返回函数的cache属性。
    注意此函数的用法,应当赋给一个函数,然后函数再去执行
  */
  _.memoize = function(func, hasher) {
    var memoize = function(key) {
      var cache = memoize.cache;
      var address = ‘‘ + (hasher ? hasher.apply(this, arguments) : key);// 计算cache中的存储key
      if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);
      return cache[address];
    };
    memoize.cache = {};
    return memoize;
  };

  // Delays a function for the given number of milliseconds, and then calls
  // it with the arguments supplied.
  /*
    过wait时间后异步执行
  */
  _.delay = restArgs(function(func, wait, args) {
    return setTimeout(function() {
      return func.apply(null, args);
    }, wait);
  });

  // Defers a function, scheduling it to run after the current call stack has
  // cleared.
  /*
    延迟调用func,知道调用栈为空
  */
  _.defer = _.partial(_.delay, _, 1);// wait为1ms

  // Returns a function, that, when invoked, will only be triggered at most once
  // during a given window of time. Normally, the throttled function will run
  // as much as it can, without ever going more than once per `wait` duration;
  // but if you‘d like to disable the execution on the leading edge, pass
  // `{leading: false}`. To disable execution on the trailing edge, ditto.
  /*
    创建并返回一个像节流阀一样的函数,当重复调用函数的时候,至少每隔 wait毫秒调用一次该函数。对于想控制一些触发频率较高的事件有帮助。
    函数节流原理:
    函数节流的原理挺简单的,估计大家都想到了,那就是定时器。当我触发一个时间时,先setTimout让这个事件延迟一会再执行,如果在这个时间间隔内又触发了事件,那我们就clear掉原来的定时器,再setTimeout一个新的定时器延迟一会执行
  */
  _.throttle = function(func, wait, options) {
    var timeout, context, args, result;
    var previous = 0;
    options = options || {};

    var later = function() {
      previous = options.leading === false ? 0 : _.now();
      timeout = null;
      result = func.apply(context, args);
      if (!timeout) context = args = null;
    };

    var throttled = function() {
      var now = _.now();
      if (!previous && options.leading === false) previous = now;
      var remaining = wait - (now - previous);// 剩余时间
      context = this;
      args = arguments;
      if (remaining <= 0 || remaining > wait) {// 如果现在距离上一次执行已经超过了wait,则直接执行
        if (timeout) {
          clearTimeout(timeout);
          timeout = null;
        }
        previous = now;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
      } else if (!timeout && options.trailing !== false) {
        timeout = setTimeout(later, remaining);// 过remaining后再执行
      }
      return result;
    };

    throttled.cancel = function() {
      clearTimeout(timeout);
      previous = 0;
      timeout = context = args = null;
    };

    return throttled;
  };

  // Returns a function, that, as long as it continues to be invoked, will not
  // be triggered. The function will be called after it stops being called for
  // N milliseconds. If `immediate` is passed, trigger the function on the
  // leading edge, instead of the trailing.
  /*
    返回 function 函数的防反跳版本, 将延迟函数的执行(真正的执行)在函数最后一次调用时刻的 wait 毫秒之后. 对于必须在一些输入(多是一些用户操作)停止到达之后执行的行为有帮助。 例如: 渲染一个Markdown格式的评论预览, 当窗口停止改变大小之后重新计算布局, 等等.
    与_.throttle函数区别在于一个是在一定频率内只能执行一次(每个频率段都可以执行);_.debounce是在一定频率的操作后,只执行最后一次(只执行一次)
  */
  _.debounce = function(func, wait, immediate) {
    var timeout, result;

    var later = function(context, args) {
      timeout = null;
      if (args) result = func.apply(context, args);// 执行函数,知道最后一次调用时刻的 wait 毫秒之后
    };

    var debounced = restArgs(function(args) {
      if (timeout) clearTimeout(timeout);// 清除之前wait时间以内的定时器
      if (immediate) {
        var callNow = !timeout;
        timeout = setTimeout(later, wait);
        if (callNow) result = func.apply(this, args);
      } else {
        timeout = _.delay(later, wait, this, args);// 延迟wait执行
      }

      return result;
    });

    debounced.cancel = function() {
      clearTimeout(timeout);
      timeout = null;
    };

    return debounced;
  };

  // Returns the first function passed as an argument to the second,
  // allowing you to adjust arguments, run code before and after, and
  // conditionally execute the original function.
  /*
    使func作为参数传给wrapper,这样一来,wrapper可以控制func的执行
  */
  _.wrap = function(func, wrapper) {
    return _.partial(wrapper, func);
  };

  // Returns a negated version of the passed-in predicate.
  /*
    返回一个新的predicate函数的否定版本
  */
  _.negate = function(predicate) {
    return function() {
      return !predicate.apply(this, arguments);
    };
  };

  // Returns a function that is the composition of a list of functions, each
  // consuming the return value of the function that follows.
  /*
    返回函数集 functions 组合后的复合函数, 也就是一个函数执行完之后把返回的结果再作为参数赋给下一个函数来执行. 以此类推. 在数学里, 把函数 f(), g(), 和 h() 组合起来可以得到复合函数 f(g(h()))。
  */
  _.compose = function() {
    var args = arguments;
    var start = args.length - 1;
    return function() {
      var i = start;
      var result = args[start].apply(this, arguments);
      while (i--) result = args[i].call(this, result);//一个小迭代
      return result;
    };
  };

  // Returns a function that will only be executed on and after the Nth call.
  /*
    创建一个函数, 只有在运行了 count 次之后才有效果. 在处理同组异步请求返回结果时, 如果你要确保同组里所有异步请求完成之后才 执行这个函数, 这将非常有用
  */
  _.after = function(times, func) {
    return function() {
      if (--times < 1) {
        return func.apply(this, arguments);
      }
    };
  };

  // Returns a function that will only be executed up to (but not including) the Nth call.
  /*
    创建一个函数,调用不超过count 次。 当count已经达到时,最后一个函数调用的结果将被记住并返回
    闭包!!!!!!
  */
  _.before = function(times, func) {
    var memo;
    return function() {
      if (--times > 0) {
        memo = func.apply(this, arguments);
      }
      if (times <= 1) func = null;
      return memo;
    };
  };

  // Returns a function that will be executed at most one time, no matter how
  // often you call it. Useful for lazy initialization.
  /*
    创建一个只能调用一次的函数。
  */
  _.once = _.partial(_.before, 2);

  _.restArgs = restArgs;

 

以上是关于underscore.js源码解析函数的主要内容,如果未能解决你的问题,请参考以下文章

underscore.js源码解析

underscore.js源码解析数组

underscore.js源码解析对象

underscore.js源码解析集合部分

underscore.js源码研究

underscore.js 源码分析5 基础函数和each函数的使用