// 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 found
point 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来实现最后一次调用。
没了