JavaScript
設計模式的作用是提高程式碼的重用性,可讀性,使程式碼更容易的維護和擴充套件
在javascript
中,函式是一類物件,這表示他可以作為引數傳遞給其他函式;此外,函式還可以提供作用域。
js
函式基礎部分:JavaScript學習總結(四)function函式部分
建立函式的語法
命名函式表示式
//命名函式表示式
var add = function add(a,b){
return a+b;
};
var foo = function bar() {
console.log(foo === bar);
};
foo();//true
可見,他們引用的是同一函式,但這隻在函式體內有效。
var foo = function bar() {};
console.log(foo === bar);//ReferenceError: bar is not defined
但是,你不能通過呼叫bar()
來呼叫該函式。
var foo = (function bar() {
console.log(foo === bar);
})();//false
函式表示式
//又名匿名函式
var add = function(a,b){
return a+b;
};
為變數 add
賦的值是函式定義本身。這樣,add
就成了一個函式,可以在任何地方呼叫。
函式的宣告
function foo(){
//code here
} //這裡可以不需要分號
在尾隨的分號中,函式表示式應總是使用分號,而函式的宣告中並不需要分號結尾。
宣告式函式與函式表示式的區別在於:在JS
的預編譯期,宣告式函式將會先被提取出來,然後才按順序執行js
程式碼:
console.log(f1);//[Function: f1]
console.log(f2);//undefined,Javascript並非完全的按順序解釋執行,而是在解釋之前會對Javascript進行一次“預編譯”,在預編譯的過程中,會把定義式的函式優先執行
function f1(){
console.log("I am f1");
}
var f2 = function (){
console.log("I am f2");
};
由於宣告函式都會在全域性作用域構造時候完成,因此宣告函式都是window
物件的屬性,這就說明為什麼我們不管在哪裡宣告函式,宣告函式最終都是屬於window
物件的原因了。
在javascript
語言裡任何匿名函式都是屬於window
物件。在定義匿名函式時候它會返回自己的記憶體地址,如果此時有個變數接收了這個記憶體地址,那麼匿名函式就能在程式裡被使用了,因為匿名函式也是在全域性執行環境構造時候定義和賦值,所以匿名函式的this
指向也是window
物件
var f2 = function (){
console.log("I am f2");
};
console.log(f2());//I am f2
(function(){
console.log(this === window);//true
})();
函式宣告與表示式
函式的提升(hoisting)
函式宣告的行為並不等同於命名函式表示式,其區別在於提升(hoisting)行為,看下面例子:
<script type="text/javascript">
//全域性函式
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以及實現者被提升
function foo(){
alert('local foo!');
}
//僅變數bar被提升,函式實現部分 並未被提升
var bar = function(){
alert('local bar!');
};
}
hoist();
</script>
對於所有變數,無論在函式體的何處進行宣告,都會在內部被提升到函式頂部。而對於函式通用適用,其原因在於函式只是分配給變數的物件。
提升
,顧名思義,就是把下面的東西提到上面。在JS
中,就是把定義在後面的東西(變數或函式)提升到前面中定義。 從上面的例子可以看出,在函式hoist
內部中的foo
和bar
移動到了頂部,從而覆蓋了全域性foo
和bar
函式。區域性函式bar
和foo
的區別在於,foo
被提升到了頂部且能正常執行,而bar()
的定義並沒有得到提升,僅有它的宣告被提升,所以,當執行bar()
的時候顯示結果為undefined
而不是作為函式來使用。
即時函式模式
函式也是物件,因此它們可以作為返回值。使用自執行函式的好處是直接宣告一個匿名函式,立即使用,省得定義一個用一次就不用的函式,而且免了命名衝突的問題,js
中沒有名稱空間的概念,因此很容易發生函式名字衝突,一旦命名衝突以最後宣告的為準。
模式一:
<script>
(function () {
var a = 1;
return function () {
alert(2);
};
}()());//彈出2,第一個圓括號自執行,第二個圓括號執行內部匿名函式
</script>
模式二:自執行函式變數的指向
<script type="text/javascript">
var result = (function () {
return 2;
})();//這裡已執行了函式
alert(result);//result 指向了由自執行函式的返回值2;如果彈出result()會出錯
</script>
模式三:巢狀函式
<script type="text/javascript">
var result = (function () {
return function () {
return 2;
};
})();
alert(result());//alert(result)的時候彈出function(){return 2}
</script>
模式四:自執行函式把它的返回值賦給變數
var abc = (function () {
var a = 1;
return function () {
return ++a;
}
})();//自執行函式把return後面的函式返回給變數
alert(abc());//如果是alert(abc)就會彈出return語句後面的程式碼;如果是abc(),則會執行return後面的函式
模式五:函式內部執行自身,遞迴
// 這是一個自執行的函式,函式內部執行自身,遞迴
function abc() { abc(); }
回撥模式
回撥函式:當你將一個函式write()
作為一個引數傳遞給另一個函式call()
時,那麼在某一時刻call()
可能會執行(或者呼叫)write()
。這種情況下,write()
就叫做回撥函式(callback function)
。
非同步事件監聽器
回撥模式有許多用途,比如,當附加一個事件監聽器到頁面上的一個元素時,實際上是提供了一個回撥函式的指標,該函式將會在事件發生時被呼叫。如:
document.addEventListener("click",console.log,false);
上面程式碼示例展示了文件單擊事件時以冒泡模式傳遞給回撥函式console.log()
的
javascript
特別適用於事件驅動程式設計,因為回撥模式支援程式以非同步方式執行。
超時
使用回撥模式的另一個例子是,當使用瀏覽器的window
物件所提供的超時方法:setTimeout()
和setInterval()
,如:
<script type="text/javascript">
var call = function(){
console.log("100ms will be asked…");
};
setTimeout(call, 100);
</script>
庫中的回撥模式
當設計一個js
庫時,回撥函式將派上用場,一個庫的程式碼應儘可能地使用可複用的程式碼,而回撥可以幫助實現這種通用化。當我們設計一個龐大的js
庫時,事實上,使用者並不會需要其中的大部分功能,而我們可以專注於核心功能並提供“掛鉤形式”的回撥函式,這將使我們更容易地構建、擴充套件,以及自定義庫方法
Curry化
Curry化
技術是一種通過把多個引數填充到函式體中,實現將函式轉換為一個新的經過簡化的(使之接受的引數更少)函式的技術。———【精通JavaScript】
簡單來說,Curry化
就是一個轉換過程,即我們執行函式轉換的過程。如下例子:
<script type="text/javascript">
//curry化的add()函式
function add(x,y){
var oldx = x, oldy = y;
if(typeof oldy == "undefined"){
return function(newy){
return oldx + newy;
};
}
//完全應用
return x+y;
}
//測試
typeof add(5);//輸出"function"
add(3)(4);//7
//建立並儲存一個新函式
var add2000 = add(2000);
add2000(10);//輸出2010
</script>
當第一次呼叫add()
時,它為返回的內部函式建立了一個閉包。該閉包將原始的x和y值儲存到私有變數oldx和oldy中。
現在,我們將可使用任意函式curry的通用方法,如:
<script type="text/javascript">
//普通函式
function add(x,y){
return x + y;
}
//將一個函式curry化以獲得一個新的函式
var newadd = test(add,5);
newadd(4);//9
//另一種選擇,直接呼叫新函式
test(add,6)(7);//輸出13
</script>
何時使用Curry化
當發現正在呼叫同一個函式時,並且傳遞的引數絕大多數都是相同的,那麼該函式可能是用於Curry化的一個很好的候選引數