本文中的內(nèi)容來自于 《JavaScript 忍者秘籍》。
函數(shù)存儲(chǔ)
利用以下代碼可以完成函數(shù)存儲(chǔ)功能码倦。
var store = {
nextId: 1,
cache: {},
add: function(fn) {
if (!fn.id) {
fn.id = store.nextId++;
return !!(store.cache[fn.id] = fn);
}
}
};
使用場景:可以用來存儲(chǔ)事件的回調(diào)函數(shù)黑竞,由于在 addEventListener
和 attachEvent
的解綁過程中都需要原樣傳入綁定的函數(shù)僻焚,所以我們可以將綁定的函數(shù)存儲(chǔ)下來,以供解綁事件時(shí)使用汁咏。
自記憶函數(shù)
所謂自記憶函數(shù),就是說函數(shù)自己能夠記住先前計(jì)算的結(jié)果作媚,這樣就能避免相同的計(jì)算執(zhí)行兩次梆暖,可以顯著的提高性能。比如說下面這個(gè)檢測(cè)是否為素?cái)?shù)的函數(shù)掂骏。
function isPrime(value) {
if (!isPrime.results) {
isPrime.results = {};
}
if (isPrime.results[value] !== undefined) {
return isPrime.results[value];
}
var prime = value !== 1;
for(var i = 2; i < value; i++) {
if (value % i === 0) {
prime = false;
break;
}
}
return isPrime.results[value] = prime;
}
緩存記憶有兩個(gè)優(yōu)點(diǎn):
- 在函數(shù)調(diào)用獲取之前計(jì)算結(jié)果的時(shí)候轰驳,最終用戶享有性能優(yōu)勢(shì)。
- 發(fā)生在幕后弟灼,完全無縫级解,最終用戶和頁面開發(fā)人員都無需特殊操作或者為此做任何額外的初始化工作。
將緩存記憶用在 DOM
的獲取操作上田绑,可以獲得 5 倍的性能提升勤哗,如下所示。
function getElements(name) {
if (!getElements.cache) {
getElements.cache = {};
}
return getElements.cache[name] =
getElements.cache[name] ||
document.getElementsByTagName(name);
}
上面我們求素?cái)?shù)的例子中掩驱,其實(shí)是在函數(shù)中對(duì)結(jié)果進(jìn)行了緩存芒划,不過值得注意的一點(diǎn)是冬竟,這種實(shí)現(xiàn)只有在我們能獲取到函數(shù)體的時(shí)候才可以使用。下面我們就對(duì)上面的函數(shù)進(jìn)行改寫民逼。
Function.prototype.memoized = function(key) {
this._values = this._values || {};
return this._values[key] !== undefined ?
this._values[key] :
this._values[key] = this.call(this, key);
};
function isPrime(num) {
var prime = num != 1;
for(var i = 2; i < num; i++) {
if (num % i === 0) {
prime = false;
break;
}
}
return prime;
}
console.log(isPrime.memoized(5));
console.log(isPrime._values[5]);
這種寫法可以解決剛才我們提出的無法獲取函數(shù)體的問題泵殴,不過又出現(xiàn)了一個(gè)問題,因?yàn)樯厦娴暮瘮?shù)要求調(diào)用者在使用 isPrime()
的時(shí)候必須要跟上 .memoized()
拼苍,不過調(diào)用者不可能時(shí)刻都能記得這一點(diǎn)笑诅,所以對(duì)于這個(gè)函數(shù)我們還可以改寫,如下所示:
Function.prototype.memoized = function(key) {
this._values = this._values || {};
return this._values[key] !== undefined ?
this._values[key] :
this._values[key] = this.call(this, key);
};
Function.prototype.memoize = function(key) {
var fn = this;
return function() {
return fn.memoized.call(fn, key);
}
};
var isPrime = (function(num) {
var prime = num !== 1;
for(var i = 2; i < num; i++) {
if (num % i === 0) {
prime = false;
break;
}
}
return prime;
}).memoize();
console.log(isPrime(5));
不過疮鲫,上面的功能都被添加在 Function
上吆你,由所有函數(shù)實(shí)例共享,如果感覺這么做有所不妥俊犯,可以使用下面這種方式妇多。
function memoize(fn) {
var cache = {};
return function(key) {
console.log("before: " + cache[key]);
return cache[key] !== undefined ?
cache[key] :
cache[key] = fn.call(this, key);
}
}
只需要對(duì)要緩存的函數(shù)進(jìn)行包裝即可。
函數(shù)判斷
一般而言燕侠,要判斷一個(gè)函數(shù)類型砌梆,只需要利用 typeof functionName
即可(會(huì)返回字符串 function
)。不過會(huì)有一些特殊情況讓我們的判斷失效贬循,比如下面幾種:
Opera
: 在HTML
的<object>
元素上使用typeof
的話咸包,會(huì)返回function
,而不是我們期望的object
杖虾。(書中說在Firefox
中會(huì)出現(xiàn)這個(gè)問題烂瘫,不過我親自檢測(cè)之后,發(fā)現(xiàn)我電腦上的Firefox
并沒有出現(xiàn)上述問題奇适,反而是Opera
出現(xiàn)了這個(gè)問題 )坟比。Safari
:Safari
認(rèn)為DOM
的NodeList
是一個(gè)function
,所以typeof document.body.childNodes == function
嚷往。(本人未親自嘗試)
基于以上情況葛账,我們需要尋找一種完美的解決方案,不過事實(shí)上并不存在完美的解決方案皮仁,倒是有一種接近完美的方案籍琳,那就是利用 Object.toString()
方法。代碼如下:
function isFunction(fn) {
return Object.prototype.toString.call(fn) === "[object Function]";
}
利用這項(xiàng)技術(shù)贷祈,還可以判斷 String, RegExp, Date
等其它對(duì)象趋急。
這里我們不直接調(diào)用 fn.toString()
的原因有兩個(gè):
不同的對(duì)象可能有自己的
toString()
方法實(shí)現(xiàn)。JavaScript
中的大多數(shù)類型都已經(jīng)有一個(gè)預(yù)定義的toString()
方法覆蓋了Object.prototype
提供的toString()
方法势誊。
從下面可以看出 String
和 Array
重寫了 Object
的 toString()
方法呜达。
var sContent = "Hello World";
console.log(sContent.toString()); // "Hello World"
var aContent = [1, 2, 3];
console.log(aContent.toString()); // "[1, 2, 3]"
剛才已經(jīng)提及,上面這個(gè)檢測(cè)的方法只是接近完美粟耻,這說明它也有失誤的情況查近,比如在 IE
中會(huì)將 DOM
元素的方法報(bào)告成 object
眉踱。
偽造數(shù)組
出于某種目的(我也不知道),我們可以將對(duì)象偽造成一個(gè)數(shù)組霜威,具體操作如下:
var eles = {
length: 0,
add: function(ele) {
Array.prototype.push.call(this, ele);
}
};
為 eles
對(duì)象添加了一個(gè) length
屬性谈喳,當(dāng)調(diào)用 push
方法時(shí),length
屬性會(huì)自動(dòng)增加侥祭。