您與互聯網業務,只差一個靠譜的服務商
常州400電話申請開通【常州企業網站建設】常州微信公眾號小程序開發運營價格、常州微信公眾號APP軟件客戶端設計運營、常州網頁頁面設計公司費用、常州公司網站制作方案流程改版維護大概需要多少錢
在創建可訪問的內部函數的函數體之外解析該內部函數就會構成閉包。這表明閉包很容易創建,但這樣一來可能會導致一種結果,即沒有認識到閉包是一種語言特性的 JavaScript 作者,會按照內部函數能完成多種任務的想法來使用內部函數。但他們對使用內部函數的結果并不明了,而且根本意識不到創建了閉包,或者那樣做意味著什么。
正如下一節談到 IE 中內存泄漏問題時所提及的,意外創建的閉包可能導致嚴重的負面效應,而且也會影響到代碼的性能。問題不在于閉包本身,如果能夠真正做到謹慎地使用它們,反而會有助于創建高效的代碼。換句話說,使用內部函數會影響到效率。
使用內部函數最常見的一種情況就是將其作為 DOM 元素的事件處理器。例如,下面的代碼用于向一個鏈接元素添加 onclick 事件處理器:
/* 定義一個全局變量,通過下面的函數將它的值 作為查詢字符串的一部分添加到鏈接的 - href - 中: */ var quantaty = 5; /* 當給這個函數傳遞一個鏈接(作為函數中的參數 - linkRef -)時, 會將一個 onclick 事件處理器指定給該鏈接,該事件處理器 將全局變量 - quantaty - 的值作為字符串添加到鏈接的 - href - 屬性中,然后返回 true 使該鏈接在單擊后定位到由 - href - 屬性包含的查詢字符串指定的資源: */ function addGlobalQueryOnClick(linkRef){ /* 如果可以將參數 - linkRef - 通過類型轉換為 ture (說明它引用了一個對象): */ if(linkRef){ /* 對一個函數表達式求值,并將對該函數對象的引用 指定給這個鏈接元素的 onclick 事件處理器: */ linkRef.onclick = function(){ /* 這個內部函數表達式將查詢字符串 添加到附加事件處理器的元素的 - href - 屬性中: */ this.href += (’?quantaty=’+escape(quantaty)); return true; }; } }
無論什么時候調用 addGlobalQueryOnClick 函數,都會創建一個新的內部函數(通過賦值構成了閉包)。從效率的角度上看,如果只是調用一兩次 addGlobalQueryOnClick 函數并沒有什么大的妨礙,但如果頻繁使用該函數,就會導致創建許多截然不同的函數對象(每對內部函數表達式求一次值,就會產生一個新的函數對象)。
上面例子中的代碼沒有關注內部函數在創建它的函數外部可以訪問(或者說構成了閉包)這一事實。實際上,同樣的效果可以通過另一種方式來完成。即單獨地定義一個用于事件處理器的函數,然后將該函數的引用指定給元素的事件處理屬性。這樣,只需創建一個函數對象,而所有使用相同事件處理器的元素都可以共享對這個函數的引用:
/* 定義一個全局變量,通過下面的函數將它的值 作為查詢字符串的一部分添加到鏈接的 - href - 中: */ var quantaty = 5; /* 當把一個鏈接(作為函數中的參數 - linkRef -)傳遞給這個函數時, 會給這個鏈接添加一個 onclick 事件處理器,該事件處理器會 將全局變量 - quantaty - 的值作為查詢字符串的一部分添加到 鏈接的 - href - 中,然后返回 true,以便單擊鏈接時定位到由 作為 - href - 屬性值的查詢字符串所指定的資源: */ function addGlobalQueryOnClick(linkRef){ /* 如果 - linkRef - 參數能夠通過類型轉換為 true (說明它引用了一個對象): */ if(linkRef){ /* 將一個對全局函數的引用指定給這個鏈接 的事件處理屬性,使函數成為鏈接元素的事件處理器: */ linkRef.onclick = forAddQueryOnClick; } } /* 聲明一個全局函數,作為鏈接元素的事件處理器, 這個函數將一個全局變量的值作為要添加事件處理器的 鏈接元素的 - href - 值的一部分: */ function forAddQueryOnClick(){ this.href += (’?quantaty=’+escape(quantaty)); return true; }
在上面例子的第一個版本中,內部函數并沒有作為閉包發揮應有的作用。在那種情況下,反而是不使用閉包更有效率,因為不用重復創建許多本質上相同的函數對象。
類似地考量同樣適用于對象的構造函數。與下面代碼中的構造函數框架類似的代碼并不罕見:
function ExampleConst(param){ /* 通過對函數表達式求值創建對象的方法, 并將求值所得的函數對象的引用賦給要創建對象的屬性: */ this.method1 = function(){ … // 方法體。 }; this.method2 = function(){ … // 方法體。 }; this.method3 = function(){ … // 方法體。 }; /* 把構造函數的參數賦給對象的一個屬性:*/ this.publicProp = param; }
每當通過 new ExampleConst(n) 使用這個構造函數創建一個對象時,都會創建一組新的、作為對象方法的函數對象。因此,創建的對象實例越多,相應的函數對象也就越多。
Douglas Crockford 提出的模仿 JavaScript 對象私有成員的技術,就利用了將對內部函數的引用指定給在構造函數中構造對象的公共屬性而形成的閉包。如果對象的方法沒有利用在構造函數中形成的閉包,那么在實例化每個對象時創建的多個函數對象,會使實例化過程變慢,而且將有更多的資源被占用,以滿足創建更多函數對象的需要。
這那種情況下,只創建一次函數對象,并把它們指定給構造函數 prototype 的相應屬性顯然更有效率。這樣一來,它們就能被構造函數創建的所有對象共享了:
function ExampleConst(param){ /* 將構造函數的參數賦給對象的一個屬性:*/ this.publicProp = param; } /* 通過對函數表達式求值,并將結果函數對象的引用 指定給構造函數原型的相應屬性來創建對象的方法: */ ExampleConst.prototype.method1 = function(){ … // 方法體。 }; ExampleConst.prototype.method2 = function(){ … // 方法體。 }; ExampleConst.prototype.method3 = function(){ … // 方法體。 };