underscore.js 755 -- 876

来源:互联网 发布:怎么才能在淘宝上卖东西 编辑:程序博客网 时间:2024/06/14 02:05
  // 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);    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.  _.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.  _.partial = restArgs(function(func, boundArgs) {    var placeholder = _.partial.placeholder;    var bound = function() {      var position = 0, length = boundArgs.length;      var args = Array(length);      for (var i = 0; i < length; i++) {        args[i] = boundArgs[i] === placeholder ? arguments[position++] : boundArgs[i];      }      while (position < arguments.length) args.push(arguments[position++]);      return executeBound(func, bound, this, this, args);    };    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.  _.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);    }  });  // Memoize an expensive function by storing its results.  _.memoize = function(func, hasher) {    var memoize = function(key) {      var cache = memoize.cache;      var address = '' + (hasher ? hasher.apply(this, arguments) : 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.  _.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.  _.defer = _.partial(_.delay, _, 1);  // 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.  _.throttle = function(func, wait, options) {    var timeout, context, args, result;    var previous = 0;    if (!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) {        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);      }      return result;    };    throttled.cancel = function() {      clearTimeout(timeout);      previous = 0;      timeout = context = args = null;    };    return throttled;  };

_.executeBound

 var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {    if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);    var self = baseCreate(sourceFunc.prototype);    var result = sourceFunc.apply(self, args);    if (_.isObject(result)) return result;    return self;  };

拆开分析

var executeBound = function (sourceFunc, boundFunc, context, callingContext, args) {} // 传入五个参数    //sourceFunc: 选定所属函数    // boundFunc    // context    // callingContext    // args: 应当为一个数组

函数内部:

if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args); // 如果没有callingContext或者不属于boundFunc时,直接对应context    var self = baseCreate(sourceFunc.prototype); //创造一个空对象,prototype = sourceFunc    var result = sourceFunc.apply(self, args); //对应该空函数调用目标函数    if (_.isObject(result)) return result; // 如果是对象,返回result    return self; // 返回空对象

_.bind

  // Create a function bound to a given object (assigning `this`, and arguments,  // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if  // available.  _.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)); //传入的this为全局,因此自动在executeBound中执行第一项    });     return bound;  });

解析网址

_.partial

  // 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.  _.partial = restArgs(function(func, boundArgs) {    var placeholder = _.partial.placeholder; // placeholder为underscore本身    var bound = function() {      var position = 0, length = boundArgs.length;      var args = Array(length);      for (var i = 0; i < length; i++) {        args[i] = boundArgs[i] === placeholder ? arguments[position++] : boundArgs[i];// 如果是,则跳过,直接让后面输入的arguments优先调入      }      while (position < arguments.length) args.push(arguments[position++]); // 将后面输入的arguments加入      return executeBound(func, bound, this, this, args);    };    return bound;  });

_.bindAll;

// 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.  _.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);    }  });

对对象中的每个属性都用_.bind,flatten先铺平

_.memoize

// Memoize an expensive function by storing its results.  _.memoize = function(func, hasher) {    var memoize = function(key) {      var cache = memoize.cache;      var address = '' + (hasher ? hasher.apply(this, arguments) : key);      if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);      return cache[address];    };    memoize.cache = {};    return memoize;  };

记录复杂函数的结果

_.memoize = function (fund, hasher) {    var memorize = function (key) {        var cache = memoize.cache;              var address = '' + (hasher ? hasher.apply(this, arguments) : key); //是否有哈希算法,无则默认用key               if (!_.has(cache, address)) cache[address] = func.apply(this, arguments); // 结果保存                     return cache[address];        memoize.cache = {};    }}

即以上操作返回一个能够缓存的函数,假如有递归,可以将每次的递归结果缓存到缓存函数一个名叫cache属性中,追踪实现。

_.delay

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

_.defer

  // Defers a function, scheduling it to run after the current call stack has  // cleared.  _.defer = _.partial(_.delay, _, 1); _.partial = restArgs(function(func, boundArgs) {    var placeholder = _.partial.placeholder;    var bound = function() {      var position = 0, length = boundArgs.length;      var args = Array(length);      for (var i = 0; i < length; i++) {        args[i] = boundArgs[i] === placeholder ? arguments[position++] : boundArgs[i];      }      while (position < arguments.length) args.push(arguments[position++]);      return executeBound(func, bound, this, this, args);    };    return bound;  });

类似于延迟为0的_.defer,为什么这样写就是为了规定仅输入一个函数即可.

_.throttle

  // 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.  _.throttle = function(func, wait, options) {    var timeout, context, args, result;    var previous = 0;    if (!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) {        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);      }      return result;    };    throttled.cancel = function() {      clearTimeout(timeout);      previous = 0;      timeout = context = args = null;    };    return throttled;  };

我们在使用throttle的时候

function test() {    console.log('point got!')}var moment = $.throttle(test, 5000)moment()moment()moment() No matching processes belonging to you were foundpoint got!(间隔一段时间)point got![Finished in 5.1s]

详情

这个throttle就是指你在一个时间段内如果不停调用该函数的话,如果正好过了那个间隔,你就可以正式调用函数的内容。

接下来我们看看究竟源码是怎么实现的

// unc, wait, options 为传参 var timeout, context, args, result;    var previous = 0;    if (!options) options = {}; 

正如所写的那样,有timeout,即时间间隔,args参数,result是结果,context作为this的绑定

先定一个previous为0,即之前的时间。

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

?这个函数干什么用的?
这里写图片描述
把timeout设定成0然后还调用一次函数??最后把result的返回值设定为调用函数???没有设定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) {        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);      }      return result;    };
 throttled.cancel = function() {      clearTimeout(timeout);       previous = 0;      timeout = context = args = null;    };    return throttled;

我们看到最后return的是一个叫throated的函数,这个函数还含有一个cancel的方法,用来清除timeout和设定previous为0的

  var now = _.now(); //获取当前时间      if (!previous && options.leading === false) previous = now; //如果不设定leading属性,我们的previous就为now      var remaining = wait - (now - previous); // 不设定leading时很有意思,remaining就是waiting,即等待的总时间      context = this;      args = arguments; //传入参数       if (remaining <= 0 || remaining > 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); //不设定leading的第一下我们设个timeout,并且用remaining来设定调用时间      }      return result; // 返回结果

哦,现在我们知道了,从一开始,我们就设定一个叫later的函数,这个函数代表最后执行的一次定时,而之前的所有,我们都用previous跟now来表示,我们用取到的now - previous来判断是否在一个时间间隔内,若在,则不调用function,若在,则调用,最后一下如果不设定trailing,则是通过setimeout来实现最后一次调用。

没了
这里写图片描述