您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)javascript函數(shù)的運(yùn)用方法,小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
作用域安全的構(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
如果沒有使用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);//undefinedconsole.log(window.name);//match
window的name屬性是用來標(biāo)識(shí)鏈接目標(biāo)和框架的,這里對(duì)該屬性的偶然覆蓋可能會(huì)導(dǎo)致頁面上的其它錯(cuò)誤,這個(gè)問題的解決方法就是創(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ì)帶來副作用。這是因?yàn)?,下列代碼中,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語句,以檢查瀏覽器特性,解決不同瀏覽器的兼容問題。比如,我們最常見的為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è)過程,在addEvent函數(shù)每次調(diào)用的時(shí)候都要走一遍,其實(shí),如果瀏覽器支持其中的一種方法,那么他就會(huì)一直支持了,就沒有必要再進(jìn)行其他分支的檢測(cè)了。也就是說,if語句不必每次都執(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)過執(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語句的每個(gè)分支都會(huì)為addEvent變量賦值,有效覆蓋了原函數(shù)。最后一步便是調(diào)用了新賦函數(shù)。下一次調(diào)用addEvent()時(shí),便會(huì)直接調(diào)用新賦值的函數(shù),這樣就不用再執(zhí)行if語句了
但是,這種方法有個(gè)缺點(diǎn),如果函數(shù)名稱有所改變,修改起來比較麻煩
2、第二種是聲明函數(shù)時(shí)就指定適當(dāng)?shù)暮瘮?shù)。 這樣在第一次調(diào)用函數(shù)時(shí)就不會(huì)損失性能了,只在代碼加載時(shí)會(huì)損失一點(diǎn)性能
以下就是按照這一思路重寫的addEvent()。以下代碼創(chuàng)建了一個(gè)匿名的自執(zhí)行函數(shù),通過不同的分支以確定應(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è)問題在于沒有保存handler.handleClick()的環(huán)境,所以this對(duì)象最后是指向了DOM按鈕而非handler
可以使用閉包來修正這個(gè)問題
<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)傳遞過去
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ù)的效用就突顯出來了。它們主要用于事件處理程序以及setTimeout()和setInterval()。然而,被綁定函數(shù)與普通函數(shù)相比有更多的開銷,它們需要更多內(nèi)存,同時(shí)也因?yàn)槎嘀睾瘮?shù)調(diào)用稍微慢一點(diǎn),所以最好只在必要時(shí)使用
函數(shù)柯里化
與函數(shù)綁定緊密相關(guān)的主題是函數(shù)柯里化(function currying),它用于創(chuàng)建已經(jīng)設(shè)置好了一個(gè)或多個(gè)參數(shù)的函數(shù)。函數(shù)柯里化的基本方法和函數(shù)綁定是一樣的:使用一個(gè)閉包返回一個(gè)函數(shù)。兩者的區(qū)別在于,當(dāng)函數(shù)被調(diào)用時(shí),返回的函數(shù)還需要設(shè)置一些傳入的參數(shù)
function add(num1,num2){ return num1+num2; }function curriedAdd(num2){ return add(5,num2); } console.log(add(2,3));//5console.log(curriedAdd(3));//8
這段代碼定義了兩個(gè)函數(shù):add()和curriedAdd()。后者本質(zhì)上是在任何情況下第一個(gè)參數(shù)為5的add()版本。盡管從技術(shù)來說curriedAdd()并非柯里化的函數(shù),但它很好地展示了其概念
柯里化函數(shù)通常由以下步驟動(dòng)態(tài)創(chuàng)建:調(diào)用另一個(gè)函數(shù)并為它傳入要柯里化的函數(shù)和必要參數(shù)。下面是創(chuàng)建柯里化函數(shù)的通用方式
function curry(fn){ var args = Array.prototype.slice.call(arguments, 1); return function(){ var innerArgs = Array.prototype.slice.call(arguments), finalArgs = args.concat(innerArgs); return fn.apply(null, finalArgs); }; }
curry()函數(shù)的主要工作就是將被返回函數(shù)的參數(shù)進(jìn)行排序。curry()的第一個(gè)參數(shù)是要進(jìn)行柯里化的函數(shù),其他參數(shù)是要傳入的值。為了獲取第一個(gè)參數(shù)之后的所有參數(shù),在arguments對(duì)象上調(diào)用了slice()方法,并傳入?yún)?shù)1表示被返回的數(shù)組包含從第二個(gè)參數(shù)開始的所有參數(shù)。然后args數(shù)組包含了來自外部函數(shù)的參數(shù)。在內(nèi)部函數(shù)中,創(chuàng)建了innerArgs數(shù)組用來存放所有傳入的參數(shù)(又一次用到了slice())。有了存放來自外部函數(shù)和內(nèi)部函數(shù)的參數(shù)數(shù)組后,就可以使用concat()方法將它們組合為finalArgs,然后使用apply()將結(jié)果傳遞給函數(shù)。注意這個(gè)函數(shù)并沒有考慮到執(zhí)行環(huán)境,所以調(diào)用apply()時(shí)第一個(gè)參數(shù)是null。curry()函數(shù)可以按以下方式應(yīng)用
function add(num1, num2){ return num1 + num2; }var curriedAdd = curry(add, 5); alert(curriedAdd(3)); //8
在這個(gè)例子中,創(chuàng)建了第一個(gè)參數(shù)綁定為5的add()的柯里化版本。當(dāng)調(diào)用cuurriedAdd()并傳入3時(shí),3會(huì)成為add()的第二個(gè)參數(shù),同時(shí)第一個(gè)參數(shù)依然是5,最后結(jié)果便是和8。也可以像下例這樣給出所有的函數(shù)參數(shù):
function add(num1, num2){ return num1 + num2; } var curriedAdd2 = curry(add, 5, 12); alert(curriedAdd2()); //17
在這里,柯里化的add()函數(shù)兩個(gè)參數(shù)都提供了,所以以后就無需再傳遞給它們了
函數(shù)柯里化還常常作為函數(shù)綁定的一部分包含在其中,構(gòu)造出更為復(fù)雜的bind()函數(shù)
function bind(fn, context){ var args = Array.prototype.slice.call(arguments, 2); return function(){ var innerArgs = Array.prototype.slice.call(arguments), finalArgs = args.concat(innerArgs); return fn.apply(context, finalArgs); }; }
對(duì)curry()函數(shù)的主要更改在于傳入的參數(shù)個(gè)數(shù),以及它如何影響代碼的結(jié)果。curry()僅僅接受一個(gè)要包裹的函數(shù)作為參數(shù),而bind()同時(shí)接受函數(shù)和一個(gè)object對(duì)象。這表示給被綁定的函數(shù)的參數(shù)是從第三個(gè)開始而不是第二個(gè),這就要更改slice()的第一處調(diào)用。另一處更改是在倒數(shù)第3行將object對(duì)象傳給apply()。當(dāng)使用bind()時(shí),它會(huì)返回綁定到給定環(huán)境的函數(shù),并且可能它其中某些函數(shù)參數(shù)已經(jīng)被設(shè)好。要想除了event對(duì)象再額外給事件處理程序傳遞參數(shù)時(shí),這非常有用
var handler = { message: "Event handled", handleClick: function(name, event){ alert(this.message + ":" + name + ":" + event.type); } }; var btn = document.getElementById("my-btn"); EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler, "my-btn"));
handler.handleClick()方法接受了兩個(gè)參數(shù):要處理的元素的名字和event對(duì)象。作為第三個(gè)參數(shù)傳遞給bind()函數(shù)的名字,又被傳遞給了handler.handleClick(),而handler.handleClick()也會(huì)同時(shí)接收到event對(duì)象
ECMAScript5的bind()方法也實(shí)現(xiàn)函數(shù)柯里化,只要在this的值之后再傳入另一個(gè)參數(shù)即可
var handler = { message: "Event handled", handleClick: function(name, event){ alert(this.message + ":" + name + ":" + event.type); } }; var btn = document.getElementById("my-btn"); EventUtil.addHandler(btn, "click", handler.handleClick.bind(handler, "my-btn"));
javaScript中的柯里化函數(shù)和綁定函數(shù)提供了強(qiáng)大的動(dòng)態(tài)函數(shù)創(chuàng)建功能。使用bind()還是curry()要根據(jù)是否需要object對(duì)象響應(yīng)來決定。它們都能用于創(chuàng)建復(fù)雜的算法和功能,當(dāng)然兩者都不應(yīng)濫用,因?yàn)槊總€(gè)函數(shù)都會(huì)帶來額外的開銷
函數(shù)重寫
由于一個(gè)函數(shù)可以返回另一個(gè)函數(shù),因此可以用新的函數(shù)來覆蓋舊的函數(shù)
function a(){ console.log('a'); a = function(){ console.log('b'); } }
這樣一來,當(dāng)我們第一次調(diào)用該函數(shù)時(shí)會(huì)console.log('a')會(huì)被執(zhí)行;全局變量a被重定義,并被賦予新的函數(shù)
當(dāng)該函數(shù)再次被調(diào)用時(shí), console.log('b')會(huì)被執(zhí)行
再復(fù)雜一點(diǎn)的情況如下所示
var a = (function(){ function someSetup(){ var setup = 'done'; } function actualWork(){ console.log('work'); } someSetup(); return actualWork; })()
我們使用了私有函數(shù)someSetup()和actualWork(),當(dāng)函數(shù)a()第一次被調(diào)用時(shí),它會(huì)調(diào)用someSetup(),并返回函數(shù)actualWork()的引用.
關(guān)于javascript函數(shù)的運(yùn)用方法就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。