程式設計的精義就是把一組需求分解成一組函式和資料結構。
函式物件
函式物件連接到Function.prototype。
韓式隱藏兩個特性。
context。
實作函式行為的程式碼。
函式建立一個 prototype 特性。
該特性具有 constructor 特性。
constructor 特性是一個函式。
函式是物件,能被儲存在變數、物件、陣列中。
函式可以被當引數傳給另一個函式,也能被另一個函式回傳。
函式也能有方法。
函式實字
函式實字有四個部分:
保留字 function
函式名稱,如果沒有名稱就為匿名函式
參數的集合,以逗號區隔
函式本體,任何運算式或函式皆可出現。
內層的函式可以取用參數及變數,也可享用外層的參數及變數,函式實字建立物件包含對外的聯繫,稱為閉包。
var add = function(a, b){
return a + b;
}
呼叫
暫緩現有函式的執行,把控制權和參數傳給新呼叫的函式。
每個函式均接受兩個額外參數:this 與 arsument
this:值由呼叫模式決定。
呼叫模式:方法呼叫、建構式呼叫、函式呼叫、apply 呼叫。
函式呼叫的運算子為一對小括號,皆在任何函式後方。
小括號包含一到多段運算式,以逗號區隔,運個運算產生引數值。
引數值被指定給函式參數。
引數和函式參數數量不對等,不會產生錯誤。
引數太多,多於引數會被忽略。
引數太少,則以 undefined 取得缺少的值。
引數不會檢查型別,任何的型別都可以傳給參數。
方法呼叫模式
函式儲存成物件特性,稱之為方法。
呼叫方法,this 與物件相繫。
呼叫的運算式包含精確式,則呼叫時視同方法。
方法可使用 this 存取物件,能從物件中擷取值、調整物件。
var myobj = {
value: 0,
increment: function (inc) {
this.value += typeof inc === ‘number’ ? inc : 1;
// 引數不是數值,則使用預設 1。
}
};
myObject.increment( );
console.log(myObject.value); //1
myObject.increment(2);
console.log(myObject.value); //3
函式呼叫模式
函式不是物件模式,只是像一般函式被呼叫。
使用此種模式,this 會繫結全域變數,這是設計上的錯誤。
呼叫內層函式,this 應該要繫結外層函式的 this。
解決方法:定義一個變數,並指定為 this,習慣命名為 that。
myObject.double = function ( ) {
var that = this;
var helper = function ( ) {
that.value = add(that.value, that.value)
};
}
myObject.double( );
document.writeln(myObject.getValue);
建構式呼叫模式
JavaScript 是一種原型繼承語言,不用類別,直接繼承其他物件的特性。
函式呼叫時加上new,建立的新物件將附有對函式的 prototype 有繫結,且 this 與新物件結合。
與字首new合用的函式稱為建構式,未附上會有編譯錯誤的狀況。
習慣上,建構函式的名稱首字應該為大寫。
apply 呼叫模式
apply 方法可以建構一個引數陣列。
第一個參數為繫結 this 的值,第二個則是參數的陣列。
var array = [3, 4];
var sum = add.apply(null, array);
var statusObject = {
status: ‘A-OK’
};
var status = Quo.prototype.get_status.apply(statusObject);
arguments 陣列
呼叫時獲取的額外參數即為 arguments 陣列。
函式可接受不特定數量的參數。
arguments 並非真正的陣列,為具有長度的類陣列。
var sum = function ( ){
var i, sum = 0;
for (i = 0 ; i < arguments.length; i += 1 ){
sum += arguments[i];
}
return sum;
};
回傳
呼叫函式,在遇到}結尾符號結束呼叫。
執行 return,函式立刻回傳,且不會執行後面敘述。
函數回傳一定會傳值,如果沒有指定就會回傳 undefind。
如果是建構式呼叫,return 值不是物件,就改為傳 this。
例外狀況
當某些狀況干擾程式流向,JavaScript 有一些機制處理那些狀況。
throw 敘述負責中斷函式執行:
需要拿到一個期待物件。
物件包含 name 和 message,也可以加入其他特性:
範例:
var add = function (a, b) {
if (typeof a !== ‘number’ || typeof b !== ‘number’) {
throw {
name: ‘TypeError’,
message: ‘add need numbers’
}
return a + b;
}
}
如果 try 有例外事件,那程式會跑 catch 的部分。
var try_it = function ( ) {
try {
add (‘seven’);
} catch (e) {
document.writeln(e.name
+ ‘ ‘ + e.message);
}
}
擴充型別
JavaScript 允許基本型別擴充。
prototype 新增方法之後,呼叫方法就可以不用再鍵入「prototype」。
擴充基本型別,可以改良語言的表達性,即使方法出現之前,被建立的值也會被擴充。
for-in 和原型的互動很差,可使用 hasOwnProperty 遮擋被繼承的特性。
遞迴
遞迴是個值或間接呼叫自己的函式。
遞迴會自我呼叫以解決子問題。
河內之塔是有名的數學謎題。(初階可以參考費波納齊數列)
有些語言提供尾遞迴最佳化的,就是函式回傳自我呼叫的結果,以迴圈取代呼叫,但 JavaScript 沒有提供,遞迴過深會讓堆疊疲憊。
範圍
範圍控制變數與參數的可見性及生命週期。
減少命名衝突
自動記憶體管理
JavaScript 沒有區塊範圍,但有區塊語法。
JavaScript 具有函式範圍,意思是定義在函式內的變數及參數,脫離函式就不可見。
因為 JavaScript 沒有區塊,所以使用變數時再宣告變數,這樣並不好。
closure
內層函式能取用外層函式的參數和變數(除了 this 和 arguments),而不是變數的拷貝。
範例:
呼叫一個回傳物件實字的函式。
把呼叫函式的結果回傳給 myObject。
函式回傳兩個方法的物件,兩個方法皆可取用 value。
var myObject = function ( ){
var value = 0;
return {
increment: function (inc) {
value += typeof inc === ‘number’ ? inc : 1;
},
getValue: function ( ) {
return value;
}
}
}( );
回呼
網路上有同步請求,會使得用戶處於凍結狀態,較佳的方式是製作非同步請求,用戶才不會被阻塞。
模組
可以使用函式搭配閉包製作模組。
模組是個函式或物件,用於呈現介面,隱藏他的狀態及實作。
使用韓式製作模組,幾乎可以全面消除全域變數。
模組利用函式範圍與閉包繫結私有關係。
常用的模組模式,一個「定義 private 變數與函式」的函式,他透過閉包取用私有變數與函式的特許值。
使用模組模式,可以削減全域變數的使用。
Cascade
鏈狀為單一敘述,連續呼叫同一物件中的許多方法。
curry
curry 結合函式與引數,而產生新的函式。
curry 建立一個對 curry 保存原始函式的結果、呼叫 curry 時的所有引數的呼叫。
curry 為單一輸入、單一輸出,目的是為了解決多參數輸入,所以必須拆分函式。
Memoization
大量重複計算,快取先前的結果。
函式能利用物件以記憶前一次的操作,因而避免不必要的工作。