JavaScript 函數(shù)模式詳解及示例
JavaScript設(shè)計(jì)模式的作用是提高代碼的重用性,可讀性,使代碼更容易的維護(hù)和擴(kuò)展
在javascript中,函數(shù)是一類對象,這表示他可以作為參數(shù)傳遞給其他函數(shù);此外,函數(shù)還可以提供作用域。
創(chuàng)建函數(shù)的語法
命名函數(shù)表達(dá)式
//命名函數(shù)表達(dá)式 var add = function add(a,b){ return a+b; }; var foo = function bar() { console.log(foo === bar); }; foo();//true
可見,他們引用的是同一函數(shù),但這只在函數(shù)體內(nèi)有效。
var foo = function bar() {}; console.log(foo === bar);//ReferenceError: bar is not defined
但是,你不能通過調(diào)用bar()來調(diào)用該函數(shù)。
var foo = (function bar() { console.log(foo === bar); })();//false
函數(shù)表達(dá)式
//又名匿名函數(shù) var add = function(a,b){ return a+b; };
為變量 add 賦的值是函數(shù)定義本身。這樣,add 就成了一個(gè)函數(shù),可以在任何地方調(diào)用。
函數(shù)的聲明
function foo(){ //code here } //這里可以不需要分號
在尾隨的分號中,函數(shù)表達(dá)式應(yīng)總是使用分號,而函數(shù)的聲明中并不需要分號結(jié)尾。
聲明式函數(shù)與函數(shù)表達(dá)式的區(qū)別在于:在JS的預(yù)編譯期,聲明式函數(shù)將會(huì)先被提取出來,然后才按順序執(zhí)行js代碼:
console.log(f1);//[Function: f1]
console.log(f2);//undefined,Javascript并非完全的按順序解釋執(zhí)行,而是在解釋之前會(huì)對Javascript進(jìn)行一次“預(yù)編譯”,在預(yù)編譯的過程中,會(huì)把定義式的函數(shù)優(yōu)先執(zhí)行
function f1(){ console.log("I am f1"); } var f2 = function (){ console.log("I am f2"); };
由于聲明函數(shù)都會(huì)在全局作用域構(gòu)造時(shí)候完成,因此聲明函數(shù)都是window對象的屬性,這就說明為什么我們不管在哪里聲明函數(shù),聲明函數(shù)最終都是屬于window對象的原因了。
在javascript語言里任何匿名函數(shù)都是屬于window對象。在定義匿名函數(shù)時(shí)候它會(huì)返回自己的內(nèi)存地址,如果此時(shí)有個(gè)變量接收了這個(gè)內(nèi)存地址,那么匿名函數(shù)就能在程序里被使用了,因?yàn)槟涿瘮?shù)也是在全局執(zhí)行環(huán)境構(gòu)造時(shí)候定義和賦值,所以匿名函數(shù)的this指向也是window對象
var f2 = function (){ console.log("I am f2"); }; console.log(f2());//I am f2 (function(){ console.log(this === window);//true })();
函數(shù)聲明與表達(dá)式
函數(shù)的提升(hoisting)
函數(shù)聲明的行為并不等同于命名函數(shù)表達(dá)式,其區(qū)別在于提升(hoisting)行為,看下面例子:
<script type="text/javascript"> //全局函數(shù) function foo(){alert("global foo!");} function bar(){alert('global bar');} function hoist(){ console.log(typeof foo);//function console.log(typeof bar);//undefined foo();//local foo! bar();//TypeError: 'undefined' is not a function //變量foo以及實(shí)現(xiàn)者被提升 function foo(){ alert('local foo!'); } //僅變量bar被提升,函數(shù)實(shí)現(xiàn)部分 并未被提升 var bar = function(){ alert('local bar!'); }; } hoist(); </script>
對于所有變量,無論在函數(shù)體的何處進(jìn)行聲明,都會(huì)在內(nèi)部被提升到函數(shù)頂部。而對于函數(shù)通用適用,其原因在于函數(shù)只是分配給變量的對象。
提升,顧名思義,就是把下面的東西提到上面。在JS中,就是把定義在后面的東西(變量或函數(shù))提升到前面中定義。 從上面的例子可以看出,在函數(shù)hoist內(nèi)部中的foo和bar移動(dòng)到了頂部,從而覆蓋了全局foo和bar函數(shù)。局部函數(shù)bar和foo的區(qū)別在于,foo被提升到了頂部且能正常運(yùn)行,而bar()的定義并沒有得到提升,僅有它的聲明被提升,所以,當(dāng)執(zhí)行bar()的時(shí)候顯示結(jié)果為undefined而不是作為函數(shù)來使用。
即時(shí)函數(shù)模式
函數(shù)也是對象,因此它們可以作為返回值。使用自執(zhí)行函數(shù)的好處是直接聲明一個(gè)匿名函數(shù),立即使用,省得定義一個(gè)用一次就不用的函數(shù),而且免了命名沖突的問題,js中沒有命名空間的概念,因此很容易發(fā)生函數(shù)名字沖突,一旦命名沖突以最后聲明的為準(zhǔn)。
模式一:
<script> (function () { var a = 1; return function () { alert(2); }; }()());//彈出2,第一個(gè)圓括號自執(zhí)行,第二個(gè)圓括號執(zhí)行內(nèi)部匿名函數(shù) </script>
模式二:自執(zhí)行函數(shù)變量的指向
<script type="text/javascript"> var result = (function () { return 2; })();//這里已執(zhí)行了函數(shù) alert(result);//result 指向了由自執(zhí)行函數(shù)的返回值2;如果彈出result()會(huì)出錯(cuò) </script>
模式三:嵌套函數(shù)
<script type="text/javascript"> var result = (function () { return function () { return 2; }; })(); alert(result());//alert(result)的時(shí)候彈出function(){return 2} </script>
模式四:自執(zhí)行函數(shù)把它的返回值賦給變量
var abc = (function () { var a = 1; return function () { return ++a; } })();//自執(zhí)行函數(shù)把return后面的函數(shù)返回給變量 alert(abc());//如果是alert(abc)就會(huì)彈出return語句后面的代碼;如果是abc(),則會(huì)執(zhí)行return后面的函數(shù)
模式五:函數(shù)內(nèi)部執(zhí)行自身,遞歸
// 這是一個(gè)自執(zhí)行的函數(shù),函數(shù)內(nèi)部執(zhí)行自身,遞歸 function abc() { abc(); }
回調(diào)模式
回調(diào)函數(shù):當(dāng)你將一個(gè)函數(shù)write()作為一個(gè)參數(shù)傳遞給另一個(gè)函數(shù)call()時(shí),那么在某一時(shí)刻call()可能會(huì)執(zhí)行(或者調(diào)用)write()。這種情況下,write()就叫做回調(diào)函數(shù)(callback function)。
異步事件監(jiān)聽器
回調(diào)模式有許多用途,比如,當(dāng)附加一個(gè)事件監(jiān)聽器到頁面上的一個(gè)元素時(shí),實(shí)際上是提供了一個(gè)回調(diào)函數(shù)的指針,該函數(shù)將會(huì)在事件發(fā)生時(shí)被調(diào)用。如:
document.addEventListener("click",console.log,false);
上面代碼示例展示了文檔單擊事件時(shí)以冒泡模式傳遞給回調(diào)函數(shù)console.log()的
javascript特別適用于事件驅(qū)動(dòng)編程,因?yàn)榛卣{(diào)模式支持程序以異步方式運(yùn)行。
超時(shí)
使用回調(diào)模式的另一個(gè)例子是,當(dāng)使用瀏覽器的window對象所提供的超時(shí)方法:setTimeout()和setInterval(),如:
<script type="text/javascript"> var call = function(){ console.log("100ms will be asked…"); }; setTimeout(call, 100); </script>
庫中的回調(diào)模式
當(dāng)設(shè)計(jì)一個(gè)js庫時(shí),回調(diào)函數(shù)將派上用場,一個(gè)庫的代碼應(yīng)盡可能地使用可復(fù)用的代碼,而回調(diào)可以幫助實(shí)現(xiàn)這種通用化。當(dāng)我們設(shè)計(jì)一個(gè)龐大的js庫時(shí),事實(shí)上,用戶并不會(huì)需要其中的大部分功能,而我們可以專注于核心功能并提供“掛鉤形式”的回調(diào)函數(shù),這將使我們更容易地構(gòu)建、擴(kuò)展,以及自定義庫方法
Curry化
Curry化技術(shù)是一種通過把多個(gè)參數(shù)填充到函數(shù)體中,實(shí)現(xiàn)將函數(shù)轉(zhuǎn)換為一個(gè)新的經(jīng)過簡化的(使之接受的參數(shù)更少)函數(shù)的技術(shù)。———【精通JavaScript】
簡單來說,Curry化就是一個(gè)轉(zhuǎn)換過程,即我們執(zhí)行函數(shù)轉(zhuǎn)換的過程。如下例子:
<script type="text/javascript"> //curry化的add()函數(shù) function add(x,y){ var oldx = x, oldy = y; if(typeof oldy == "undefined"){ return function(newy){ return oldx + newy; }; } //完全應(yīng)用 return x+y; } //測試 typeof add(5);//輸出"function" add(3)(4);//7 //創(chuàng)建并存儲(chǔ)一個(gè)新函數(shù) var add2000 = add(2000); add2000(10);//輸出2010 </script>
當(dāng)?shù)谝淮握{(diào)用add()時(shí),它為返回的內(nèi)部函數(shù)創(chuàng)建了一個(gè)閉包。該閉包將原始的x和y值存儲(chǔ)到私有變量oldx和oldy中。
現(xiàn)在,我們將可使用任意函數(shù)curry的通用方法,如:
<script type="text/javascript"> //普通函數(shù) function add(x,y){ return x + y; } //將一個(gè)函數(shù)curry化以獲得一個(gè)新的函數(shù) var newadd = test(add,5); newadd(4);//9 //另一種選擇,直接調(diào)用新函數(shù) test(add,6)(7);//輸出13 </script>
何時(shí)使用Curry化
當(dāng)發(fā)現(xiàn)正在調(diào)用同一個(gè)函數(shù)時(shí),并且傳遞的參數(shù)絕大多數(shù)都是相同的,那么該函數(shù)可能是用于Curry化的一個(gè)很好的候選參數(shù)
- js面向?qū)ο笾R妱?chuàng)建對象的幾種方式(工廠模式、構(gòu)造函數(shù)模式、原型模式)
- javascript組合使用構(gòu)造函數(shù)模式和原型模式實(shí)例
- 深入理解JavaScript系列(26):設(shè)計(jì)模式之構(gòu)造函數(shù)模式詳解
- JavaScript中的函數(shù)模式詳解
- JavaScript函數(shù)模式詳解
- JS面向?qū)ο蠡A(chǔ)講解(工廠模式、構(gòu)造函數(shù)模式、原型模式、混合模式、動(dòng)態(tài)原型模式)
- JavaScript面向?qū)ο笤O(shè)計(jì)二 構(gòu)造函數(shù)模式
相關(guān)文章
再JavaScript的jQuery庫中編寫動(dòng)畫效果的指南
這篇文章主要介紹了再JavaScript的jQuery庫中編寫動(dòng)畫效果的指南,包括一些內(nèi)建的效果方法的使用示例,需要的朋友可以參考下2015-08-08javascript 觸發(fā)事件列表 比較不錯(cuò)
javascript 觸發(fā)事件列表,整理的一些js的事件,一般常用的就是onclick,onmouseover等。大家可以了解下,方便以后應(yīng)用。2009-09-09Internet Explorer 11 瀏覽器介紹:別叫我IE
這篇文章主要介紹了Internet Explorer 11 瀏覽器介紹:別叫我IE,需要的朋友可以參考下2014-09-09簡單掌握J(rèn)avaScript中const聲明常量與變量的用法
const和let一樣,也是ES6版本中引入的新關(guān)鍵字,下面我們就通過例子來簡單掌握J(rèn)avaScript中const關(guān)鍵詞聲明常量與變量的用法2016-05-05javascript中attribute和property的區(qū)別詳解
這篇文章主要介紹了javascript中attribute和property的區(qū)別詳解,attribute和property對新手來說,特別容易混淆概念,本文就清晰的講解了它們的區(qū)別,需要的朋友可以參考下2014-06-06分享javascript實(shí)現(xiàn)的冒泡排序代碼并優(yōu)化
本文給大家匯總介紹了幾個(gè)個(gè)人收藏的JavaScript實(shí)現(xiàn)冒泡排序的代碼,都是非常的不錯(cuò),有需要的小伙伴可以參考下2016-06-06JS數(shù)組遍歷中for,for in,for of,map,forEach各自的使用方法與優(yōu)缺點(diǎn)
這篇文章主要介紹了JS數(shù)組遍歷中for,for in,for of,map,forEach各自的使用方法與優(yōu)缺點(diǎn),需要的朋友可以參考下2022-01-01javascript函數(shù)作用域?qū)W習(xí)示例(js作用域)
javascript中沒有塊級作用域,取而代之的javascript使用的是函數(shù)作用域,下面使用示例學(xué)習(xí)一下js作用域的使用方法2014-01-01