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