前言
函數用於指定對象的行爲,是JavaScript的基礎單元。所謂編程,就是將一組需求分解爲一組函數和數據結構的功能。
1、函數對象
JavaScript中的函數就是對象。每個函數在創建時會附加兩個隱藏的屬性:函數的上下文和實現函數調用的代碼。
函數可以像其他的值一樣被使用,可以被保存在另一個對象中,可以作爲參數傳遞,可以返回函數,還可以有方法,唯一不同的是可以被調用。
2、函數字面量
通過函數字面量創建函數:
var add = function (a, b) {
return a + b;
}
函數由以下幾個部分組成:
function保留字;函數名,function後面跟函數名,可省略(省略函數名的稱爲匿名函數);
參數,圓括號中的參數;
函數主體,花括號裏面的一組語句,在函數調用時執行。
3、調用
函數在調用時接收兩個附加的參數:this和argument。函數調用符是函數值表達式後的一對圓括號。
argument是實際參數,當實際參數與形式參數個數不匹配時,程序不會報錯。實際參數值過多,超出的參數會被省略,實際參數值過少,缺少的參數會被替換爲undefined。
函數有四種調用方式:方法調用模式、函數調用模式、構造器調用模式、apply調用模式。
4、方法調用模式
函數作爲對象的一個屬性時,稱爲方法。當一個方法被調用時,this綁定爲該對象。
var myObject = {
value: 0,
increment: function (inc) {
this.value + = typeof inc === 'number' ? inc : 1;
}
}
<pre name="code" class="javascript">myObject.increment(2);
5、函數調用模式
一個函數不作爲對象的屬性時,就是函數調用方式。
此模式下,this綁定到全局對象。有時引用內部函數時,會帶來不必要的錯誤,解決方法將this傳遞給另一個變量。
var sum = add(3, 4);
var that = this;
6、構造器調用模式
構造器調用模式,使用new來調用一個函數,創建一個連接到該函數prototype成員的新對象。
函數創建的目的就是以這種方式調用,稱爲構造器函數。構造器函數第一個字母需要大寫。如果沒有使用new來調用構造器函數不會報錯,因此不推薦使用構造器調用模式。
7、Apply調用模式
函數具有apply方法,可以指定this的值。
apply接收兩個參數,第一個參數是this的值,第二個參數是傳遞的參數數組。
8、參數
函數調用時,會得到一個參數——arguments數組,這個數組是調用函數時傳遞進來的參數列表,包括多餘的參數。
arguments不是一個真正的數組,僅包含length屬性,沒有其他任何數組的方法。
9、返回
return可以提前使函數提前返回。
一個函數總會返回一個值,如果沒有指定返回值,則返回undefined。
使用new前綴調用函數,且返回值不是一個對象,則返回this(該新對象)。
10、異常
JavaScript 提供了一套異常處理機制。
throw 語句可以中斷函數的執行,拋出的 exception 對象具有兩個屬性,異常類型 name 屬性和描述性的 message 屬性。
try...catch 語句中,如果在 try 中遇到異常,程序會跳到 catch 中繼續執行。一個 try 語句只有一個捕獲異常的 catch 代碼塊。
11、擴充類型的功能
JavaScript允許給語言的基本類型擴充功能。給Object.propertype添加方法,讓該方法對所有對象都可用。這種方式同樣對函數(Function)、數組(Array)、字符(String)、數字(Number)、正則表達式和布爾值同樣適用。
舉例,給Function.propertype增加一個method方法,下次再給對象增加方法時,可用省略propertype。
Function.propertype.method = function (name, func) {
this.propertype[name] = func;
return this;
}
<pre name="code" class="javascript">String.method('trim', function (){
return this.replace(/^s+|\s+$/g, '');
})<span style="font-family: Arial, Helvetica, sans-serif;">;</span>
12、遞歸
遞歸函數就是直接或間接調用自身的一種函數。
一個問題能分解爲一組相似的問題,每個問題都能用一個簡單的方法去解決,遞歸就是調用自身去解決它的子問題。
舉例,遍歷DOM中的所有節點,每個節點都執行函數func。
var walk_the_DOM = function walk(node, func) {
func(node); // 從節點node開始
node = node.firstChild; //<span style="font-family: Arial, Helvetica, sans-serif;">找到下一個子節點</span>
while (node) {
walk(node, func); //對子節點執行函數
node = node.nextSibling; //找到下一個子節點
}
}
尾遞歸,會返回自身調用的結果,是一種在函數的最後執行遞歸調用語句特殊形式的遞歸。(個人理解:尾遞歸是在遞歸的最後調用一個特殊的語句,而不同於找不到子節點或堆棧溢出而結束遞歸。)
尾遞歸優化,函數返回自身遞歸調用的結果,調用過程會被替換成一個循環,可以顯著提高速度。
13、作用域
作用域控制着變量與參數的可見性及生命週期。
JavaScript 支持函數作用域,在函數中的參數和變量在函數外部是不可見的,在一個函數的內部任何位置定義的變量,在該函數內部任何地方都可見。作用域的好處是內部函數可以訪問它們的外部函數的參數和變量(除了this和arguments)。
14、閉包
內部函數擁有比它的外部函數更長的生命週期。內部函數可以訪問它被創建時所處的上下文環境,這被稱爲閉包。
15、回調
網絡上的同步請求時間過長會導致客戶端進入假死狀態,發起異步請求,提供一個當服務器的響應到達時隨時出發的回調函數,客戶端就不會阻塞。
16、模塊
使用函數和閉包來構造模塊。
模塊模式利用了函數作用域和閉包來創建被綁定對象與私有成員的關聯。
模塊模式的一般形式是:一個定義了私有變量和函數的函數;利用閉包創建可以訪問私有變量和函數的特權函數;最有返回這個特權函數,或者把它們保存到一個可訪問到的地方。
var serial_marker = function (argument) {
var prefix = ''; // 私有變量
var seq = 0; // <span style="font-family: Arial, Helvetica, sans-serif;">私有變量</span>
return {
set_prefix: function (p) {
prefix = String(p);
},
set_seq: function (s) {
seq = s;
},
gensym: function () {
var result = prefix + seq;
seq += 1;
return result;
}
}
}
var seqer = serial_marker();
seqer.set_prefix('Q');
seqer.set_seq(1000);
var unique = seqer.gensym();
document.writeln('1. unique: '+unique+'<br/>'); // 1. unique: Q1000
// 即使增加了新的方法,不能更改私有變量的值
seqer.newset_prefix = function (p) {
prefix = String(p);
}
seqer.newset_prefix('W') //<span style="font-family: Arial, Helvetica, sans-serif;"> 即使增加了新的方法,不能更改私有變量的值</span>
var unique = seqer.gensym();
document.writeln('2. unique: '+unique); // 2. unique: Q1001
17、級聯
有一些方法是沒有返回值的,比如一些修改或者設置對象的某個狀態的方法,不需要返回值。可以讓這些方法可以返回 this 而不是 undefined ,就可以啓用級聯。這些方法每一個都返回該對象,每次返回的結果都可以被下一次調用。
getElement('myBoxDiv')
.move(350, 150)
.width(100)
.height(100)
.color('red')
18、柯里化
調用柯里化方法生成新函數,調用新函數時,是返回調用原始函數的結果,傳遞調用柯里化方法的參數加上調用新函數的參數。
柯里化方法的新函數調用 = 調用原始函數 + ( 柯里化方法的參數 + 新函數調用參數 )
Function.method('curry', function () {
var slice = Array.prototype.slice,
args = slice.apply(arguments), // arguments不是真正的數組,除length方法外無數組的其他方法
// args現在是真正的數組,有數組的方法
that = this;
// 返回柯里化新函數
return function () {
// 新函數包含原函數調用
// 和柯里化函數時的參數
// 和調用新函數時的參數
return that.apply(null, args.contact(slice.apply(arguments)))
};
});
19、記憶
函數將先操作的結果記錄在某個對象裏,避免無謂的重複運算,這種優化被稱爲記憶。