前言
在平時的開發(fā)中,經(jīng)常會遇到一些特殊情況,就需要一些招式來應(yīng)對。這里總結(jié)一下自己常用到的一些小招式,有不足歡迎大家指出。
模塊
var fuc1 = (function() {
var a = 'hello ';
return function(str) {
return a + str;
}
})()
fuc1('world'); // hello world
模塊模式的一般形式是:一個定義了私有變量和函數(shù);利用閉包創(chuàng)建可以訪問私有變量和函數(shù)的特權(quán)函數(shù);最后返回這個特權(quán)函數(shù),或者把它們保存到一個可訪問的地方。
該招式有以下應(yīng)用場景
協(xié)調(diào)異步
代碼需要在兩個異步fuc1與fuc2都執(zhí)行結(jié)束后再執(zhí)行,
var manager = (function() {
var count = 0;
return function() {
if(++count === 2){
// logic 3
}
}
})()
function fuc1() {
//...
}
function fuc2() {
//...
}
setTimeout(function() {
fuc1();
manager();
}, 10)
setTimeout(function() {
fuc2();
manager();
}, 10)
兼容測試
var createXHR = (function () {
if(typeof XMLHttpRequest != "undefined") {
return function () {
return new XMLHttpRequest();
}
}else if(typeof ActiveXObject != "undefined") {
return function () {
if(typeof arguments.callee.activeXString != "string") {
var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"],
i,len;
for(1 = 0, len = versions.length; i < len; i++){
try{
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
}catch(ex){
// ...
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
}
}else{
return function() {
throw new Error("NO XHR object available.");
}
}
})();
利用私有變量
var parse = (function() {
var config = ['I', 'II', 'III', 'IV'];
return function(num) {
return config[num - 1]
}
})()
parse(1); // I
parse(4); // IV
柯里化
柯里化聽起來很高大上,然而也就是Currying的音譯,千萬別被名稱嚇跑了。
它是把接受多個參數(shù)的函數(shù)中的參數(shù)拆分成兩次調(diào)用來傳遞并返回結(jié)果的新函數(shù)。
舉一個簡單的例子理解一下:
var add = function(a, b) {
return a + b
}
add(1, 2) // 3
var currying = function(a) {
return function(b) {
return a + b
}
}
currying(1)(2) // 3
add 為 接受多個參數(shù)的函數(shù),currying是將add中的參數(shù)分兩次調(diào)用傳入并返回結(jié)果的新函數(shù)。
仔細想想,它其實是模塊的衍生招式。模塊的私有變量是預(yù)先定義好的,而柯里化是保存第一次調(diào)用時傳入的參數(shù)為私有變量。因此它倆的區(qū)別是私有變量定義時機不同。
下面是一個利用柯里化實現(xiàn)的累加方法
var currying = function (fn) {
var args = [];
return function () {
var innerArgs = Array.prototype.slice.call(arguments);
if(innerArgs.length === 0){
return fn.apply(null, args)
}
args = args.concat(innerArgs);
return arguments.callee;
}
}
var mutil = function () {
var num = 0, i = 0;
while(arguments[i]){
num += arguments[i++]
}
return num
}
var add = currying(mutil);
add(1,2)(3);
add(4);
add(); // 10
函數(shù)節(jié)流
某些代碼不可以在沒有間斷的情況下連續(xù)重復(fù)執(zhí)行,所有就需要節(jié)流。比如:拖動某DOM元素時,需要動態(tài)改變元素的 x y ,就需要用到節(jié)流函數(shù)了。
function throttle(method, delay) {
var timer = null;
return function () {
var that = this, args = arguments;
clearTimeout(method.tId);
timer = setTimeout(function () {
method.apply(that, args);
clearTimeout(timer);
}, delay);
}
}
window.onresize = throttle(resizeHandle, 50)
使用該函數(shù)時你會發(fā)現(xiàn)一個問題:不斷拖動元素的過程中元素是不動的,只有停頓一下元素才跟著動。
這樣的體驗很糟糕,為了提高性能、改善體驗,需要升級一下節(jié)流函數(shù)。設(shè)置一個最大時間間隔來確保在拖動的過程中,元素可以跟隨移動。
function throttle(method, context, delay, maxInterval) {
var _start,_diff,timer;
return function () {
var that = this,
args = arguments,
_now = +new Date();
clearTimeout(timer);
_start || (_start = _now);
if(_now - _start >= maxInterval){
method.apply(that, args)
}else{
timer = setTimeout(function () {
method.apply(that, args)
}, delay)
}
}
}
window.onresize = throttle(resizeHandle, 50, 100)
延遲加載處理
在業(yè)務(wù)中經(jīng)常會遇到一些不需要同步處理的,又比較耗時的邏輯。對于這些邏輯可以采用數(shù)據(jù)分塊(array chunking)的技術(shù),小塊小塊地處理數(shù)組,通常每次一小塊??梢詣?chuàng)建一個“待辦事項”隊列,然后使用定時器逐個取出“事項”,接著設(shè)置另一個定時器。
function chunk(array, process, context) {
setTimeout(function() {
var item = array.shift();
process.call(context, item);
if(array.length > 0){
setTimeout(arguments.callee, 100);
}
}, 100)
}
記憶函數(shù)
函數(shù)可以將先前操作的結(jié)果記錄在某個對象里,從而避免重復(fù)運算,比如計算階乘函數(shù)。
function memoizer(memory, handle) {
return function (n) {
var result = memory[n],
recur = arguments.callee;
// 已運算過則直接返回
if(!result){
result = handle(recur ,n);
memory[n] = result;
}
return result;
}
}
var memory = [1, 1],
factorial = memoizer(memory, function (recur, n) {
return n * recur(n - 1)
});
factorial(4);
console.log(memory); // [1, 1, 2, 6, 24]