javascript函數(shù)中的3個(gè)高級(jí)技巧
前面的話
函數(shù)對(duì)任何一門語(yǔ)言來(lái)說(shuō)都是一個(gè)核心的概念,在javascript中更是如此。前面曾以深入理解函數(shù)系列的形式介紹了函數(shù)的相關(guān)內(nèi)容,本文將再深入一步,介紹函數(shù)的3個(gè)高級(jí)技巧
技巧一:作用域安全的構(gòu)造函數(shù)
構(gòu)造函數(shù)其實(shí)就是一個(gè)使用new操作符調(diào)用的函數(shù)
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; } var person=new Person('match',28,'Software Engineer'); console.log(person.name);//match
如果沒(méi)有使用new操作符,原本針對(duì)Person對(duì)象的三個(gè)屬性被添加到window對(duì)象
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; } var person=Person('match',28,'Software Engineer'); console.log(person);//undefined console.log(window.name);//match
window的name屬性是用來(lái)標(biāo)識(shí)鏈接目標(biāo)和框架的,這里對(duì)該屬性的偶然覆蓋可能會(huì)導(dǎo)致頁(yè)面上的其它錯(cuò)誤,這個(gè)問(wèn)題的解決方法就是創(chuàng)建一個(gè)作用域安全的構(gòu)造函數(shù)
function Person(name,age,job){ if(this instanceof Person){ this.name=name; this.age=age; this.job=job; }else{ return new Person(name,age,job); } } var person=Person('match',28,'Software Engineer'); console.log(window.name); // "" console.log(person.name); //'match' var person= new Person('match',28,'Software Engineer'); console.log(window.name); // "" console.log(person.name); //'match'
但是,對(duì)構(gòu)造函數(shù)竊取模式的繼承,會(huì)帶來(lái)副作用。這是因?yàn)椋铝写a中,this對(duì)象并非Polygon對(duì)象實(shí)例,所以構(gòu)造函數(shù)Polygon()會(huì)創(chuàng)建并返回一個(gè)新的實(shí)例
function Polygon(sides){ if(this instanceof Polygon){ this.sides=sides; this.getArea=function(){ return 0; } }else{ return new Polygon(sides); } } function Rectangle(wifth,height){ Polygon.call(this,2); this.width=this.width; this.height=height; this.getArea=function(){ return this.width * this.height; }; } var rect= new Rectangle(5,10); console.log(rect.sides); //undefined
如果要使用作用域安全的構(gòu)造函數(shù)竊取模式的話,需要結(jié)合原型鏈繼承,重寫Rectangle的prototype屬性,使它的實(shí)例也變成Polygon的實(shí)例
function Polygon(sides){ if(this instanceof Polygon){ this.sides=sides; this.getArea=function(){ return 0; } }else{ return new Polygon(sides); } } function Rectangle(wifth,height){ Polygon.call(this,2); this.width=this.width; this.height=height; this.getArea=function(){ return this.width * this.height; }; } Rectangle.prototype= new Polygon(); var rect= new Rectangle(5,10); console.log(rect.sides); //2
技巧二:惰性載入函數(shù)
因?yàn)楦鳛g覽器之間的行為的差異,我們經(jīng)常會(huì)在函數(shù)中包含了大量的if語(yǔ)句,以檢查瀏覽器特性,解決不同瀏覽器的兼容問(wèn)題。比如,我們最常見(jiàn)的為dom節(jié)點(diǎn)添加事件的函數(shù)
function addEvent(type, element, fun) { if (element.addEventListener) { element.addEventListener(type, fun, false); } else if(element.attachEvent){ element.attachEvent('on' + type, fun); } else{ element['on' + type] = fun; } }
每次調(diào)用addEvent函數(shù)的時(shí)候,它都要對(duì)瀏覽器所支持的能力進(jìn)行檢查,首先檢查是否支持addEventListener方法,如果不支持,再檢查是否支持attachEvent方法,如果還不支持,就用dom0級(jí)的方法添加事件。這個(gè)過(guò)程,在addEvent函數(shù)每次調(diào)用的時(shí)候都要走一遍,其實(shí),如果瀏覽器支持其中的一種方法,那么他就會(huì)一直支持了,就沒(méi)有必要再進(jìn)行其他分支的檢測(cè)了。也就是說(shuō),if語(yǔ)句不必每次都執(zhí)行,代碼可以運(yùn)行的更快一些。
解決方案就是惰性載入。所謂惰性載入,指函數(shù)執(zhí)行的分支只會(huì)發(fā)生一次
有兩種實(shí)現(xiàn)惰性載入的方式
【1】第一種是在函數(shù)被調(diào)用時(shí),再處理函數(shù)。函數(shù)在第一次調(diào)用時(shí),該函數(shù)會(huì)被覆蓋為另外一個(gè)按合適方式執(zhí)行的函數(shù),這樣任何對(duì)原函數(shù)的調(diào)用都不用再經(jīng)過(guò)執(zhí)行的分支了
我們可以用下面的方式使用惰性載入重寫addEvent()
function addEvent(type, element, fun) { if (element.addEventListener) { addEvent = function (type, element, fun) { element.addEventListener(type, fun, false); } } else if(element.attachEvent){ addEvent = function (type, element, fun) { element.attachEvent('on' + type, fun); } } else{ addEvent = function (type, element, fun) { element['on' + type] = fun; } } return addEvent(type, element, fun); }
在這個(gè)惰性載入的addEvent()中,if語(yǔ)句的每個(gè)分支都會(huì)為addEvent變量賦值,有效覆蓋了原函數(shù)。最后一步便是調(diào)用了新賦函數(shù)。下一次調(diào)用addEvent()時(shí),便會(huì)直接調(diào)用新賦值的函數(shù),這樣就不用再執(zhí)行if語(yǔ)句了
但是,這種方法有個(gè)缺點(diǎn),如果函數(shù)名稱有所改變,修改起來(lái)比較麻煩
【2】第二種是聲明函數(shù)時(shí)就指定適當(dāng)?shù)暮瘮?shù)。 這樣在第一次調(diào)用函數(shù)時(shí)就不會(huì)損失性能了,只在代碼加載時(shí)會(huì)損失一點(diǎn)性能
以下就是按照這一思路重寫的addEvent()。以下代碼創(chuàng)建了一個(gè)匿名的自執(zhí)行函數(shù),通過(guò)不同的分支以確定應(yīng)該使用哪個(gè)函數(shù)實(shí)現(xiàn)
var addEvent = (function () { if (document.addEventListener) { return function (type, element, fun) { element.addEventListener(type, fun, false); } } else if (document.attachEvent) { return function (type, element, fun) { element.attachEvent('on' + type, fun); } } else { return function (type, element, fun) { element['on' + type] = fun; } } })();
技巧三:函數(shù)綁定
在javascript與DOM交互中經(jīng)常需要使用函數(shù)綁定,定義一個(gè)函數(shù)然后將其綁定到特定DOM元素或集合的某個(gè)事件觸發(fā)程序上,綁定函數(shù)經(jīng)常和回調(diào)函數(shù)及事件處理程序一起使用,以便把函數(shù)作為變量傳遞的同時(shí)保留代碼執(zhí)行環(huán)境
<button id="btn">按鈕</button> <script> var handler={ message:"Event handled.", handlerFun:function(){ alert(this.message); } }; btn.onclick = handler.handlerFun; </script>
上面的代碼創(chuàng)建了一個(gè)叫做handler的對(duì)象。handler.handlerFun()方法被分配為一個(gè)DOM按鈕的事件處理程序。當(dāng)按下該按鈕時(shí),就調(diào)用該函數(shù),顯示一個(gè)警告框。雖然貌似警告框應(yīng)該顯示Event handled,然而實(shí)際上顯示的是undefiend。這個(gè)問(wèn)題在于沒(méi)有保存handler.handleClick()的環(huán)境,所以this對(duì)象最后是指向了DOM按鈕而非handler
可以使用閉包來(lái)修正這個(gè)問(wèn)題
<button id="btn">按鈕</button> <script> var handler={ message:"Event handled.", handlerFun:function(){ alert(this.message); } }; btn.onclick = function(){ handler.handlerFun(); } </script>
當(dāng)然這是特定于此場(chǎng)景的解決方案,創(chuàng)建多個(gè)閉包可能會(huì)令代碼難以理解和調(diào)試。更好的辦法是使用函數(shù)綁定
一個(gè)簡(jiǎn)單的綁定函數(shù)bind()接受一個(gè)函數(shù)和一個(gè)環(huán)境,并返回一個(gè)在給定環(huán)境中調(diào)用給定函數(shù)的函數(shù),并且將所有參數(shù)原封不動(dòng)傳遞過(guò)去
function bind(fn,context){ return function(){ return fn.apply(context,arguments); } }
這個(gè)函數(shù)似乎簡(jiǎn)單,但其功能是非常強(qiáng)大的。在bind()中創(chuàng)建了一個(gè)閉包,閉包使用apply()調(diào)用傳入的函數(shù),并給apply()傳遞context對(duì)象和參數(shù)。當(dāng)調(diào)用返回的函數(shù)時(shí),它會(huì)在給定環(huán)境中執(zhí)行被傳入的函數(shù)并給出所有參數(shù)
<button id="btn">按鈕</button> <script> function bind(fn,context){ return function(){ return fn.apply(context,arguments); } } var handler={ message:"Event handled.", handlerFun:function(){ alert(this.message); } }; btn.onclick = bind(handler.handlerFun,handler); </script>
ECMAScript5為所有函數(shù)定義了一個(gè)原生的bind()方法,進(jìn)一步簡(jiǎn)化了操作。
只要是將某個(gè)函數(shù)指針以值的形式進(jìn)行傳遞,同時(shí)該函數(shù)必須在特定環(huán)境中執(zhí)行,被綁定函數(shù)的效用就突顯出來(lái)了。它們主要用于事件處理程序以及setTimeout()和setInterval()。然而,被綁定函數(shù)與普通函數(shù)相比有更多的開銷,它們需要更多內(nèi)存,同時(shí)也因?yàn)槎嘀睾瘮?shù)調(diào)用稍微慢一點(diǎn),所以最好只在必要時(shí)使用。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
jquery單行文字向上滾動(dòng)效果的實(shí)現(xiàn)代碼
這篇文章主要介紹了jquery單行文字向上滾動(dòng)效果的具體實(shí)現(xiàn),此效果適應(yīng)于很多場(chǎng)景,會(huì)的不會(huì)的都要學(xué)習(xí)下啊2014-09-09javascript實(shí)現(xiàn)3D變換的立體圓圈實(shí)例
這篇文章主要介紹了javascript實(shí)現(xiàn)3D變換的立體圓圈效果,涉及javascript動(dòng)態(tài)操作頁(yè)面元素實(shí)現(xiàn)滾動(dòng)與變色的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-08-08threejs太陽(yáng)光與陰影效果實(shí)例代碼
在Threejs中類似于我們現(xiàn)實(shí)世界,物體顯示的顏色是由物體本身的顏色及光照的顏色相互疊加而得到的,這篇文章主要給大家介紹了關(guān)于threejs太陽(yáng)光與陰影效果的相關(guān)資料,需要的朋友可以參考下2022-04-04JS簡(jiǎn)單實(shí)現(xiàn)父子窗口傳值功能示例【未使用iframe框架】
這篇文章主要介紹了JS簡(jiǎn)單實(shí)現(xiàn)父子窗口傳值功能,結(jié)合具體實(shí)例形式分析了javascript實(shí)現(xiàn)不使用iframe框架進(jìn)行窗口之間簡(jiǎn)單傳值的相關(guān)操作技巧,需要的朋友可以參考下2017-09-09結(jié)合?ES6?類編寫JavaScript?創(chuàng)建型模式
這篇文章主要介紹了結(jié)合ES6類編寫JavaScript創(chuàng)建型模式,本文開始系統(tǒng)性的對(duì)20多種JavaScript?設(shè)計(jì)模式進(jìn)行簡(jiǎn)單概述,然后結(jié)合ES6類的方式來(lái)編寫實(shí)例代碼展示其使用方式,需要的朋友可以參考一下2022-07-07bootstrap Table服務(wù)端處理分頁(yè)(后臺(tái)是.net)
這篇文章主要為大家詳細(xì)介紹了bootstrap Table服務(wù)端處理分頁(yè),后臺(tái)是.net,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10