JavaScript語言精粹_第四章
4.1 函數對象
在JavaScript中,函數就是對象。對象是“名/值”對的集合並擁有一個連到原型對象的隱藏鏈接。對象字面量產生的對象連接到Object.prototype。函數對象連接到Function.prototype(該原型對象本身連接到object.prototype)
每個函數對象在創建是也隨帶一個prototype屬性,它的值是一個擁有constructor屬性且至即為該函數的對象
函數可以存放變量,可以被當作參數傳遞給其他函數,也可以在返回函數。
因為函數是對象,所以它可以像其他的值一樣被使用,也可以擁有方法
4.3 調用
調用一個函數將暫停當前函數的執行,傳遞控制權和參數給新函數。每個函數接受兩個附加的參數:this和arguments。
在JavaScript中有四種調用模式:方法調用模式,函數調用模式,構造器調用模式和apply調用模式。這些模式在如何初始化關鍵參數this上存在差異。
方法調用模式
函數保存為對象的一個屬性時,它被稱為方法。方法可以通過this去訪問,this到對象的綁定發生在調用的時候,這個“超級”遲綁定使得函數可以對this高度復用。
函數調用模式
當一個函數並非一個對象的屬性時,它被當作一個函數來調用(在對象的方法中再次定義的方法)。
當函數以此模式調用時,this被綁定到全局對象(這是一個bug),可以該方法定義一個變量並給它賦值為this,那麽內部函數就可以通過那個變量訪問到this
構造器調用模式
如果在一個函數前面帶上new來調用,那麽將創建一個隱藏鏈接到該函數的prototype成員的新對象,同時this將對綁定到那個新對象上。
結合new前綴調用的函數被稱為構造器函數,它們保存在以大寫格式命名的變量裏,如果調用構造器函數時沒有在前面加上new,可能會發生非常糟糕的事情(在其他地方報錯,可能你要改一天。。。),既沒有編譯時警告,也沒有運行時警告,所以大寫約定非常重要。
Apply調用模式
apply讓我們構建一個參數數組並用其去調用函數,它允許我們選擇this的值。apply方法接收兩個參數,第一個是將綁定給this的值,第二個就是參數數組
4.4 參數
當函數被調用時,會得到arguments數組,這使得編寫一個無須指定參數個數的函數成為可能。
可以構造一個將很多個值相加的函數。
因為這個設計錯誤,使得arguments並非一個真正的數組,它只是一個“類似數組”的對象,它擁有length屬性,但缺少所有的數組方法。
4.5 返回
return語句可以用來是函數提前返回。一個函數總是會返回一個值,如果沒有制定返回值,則返回undefined。
如果函數以在前面加上new前綴的方式來調用,且返回值不是一個對象,則返回this(該新對象)
4.7 給類型增加方法
JavaScript允許給語言的基本類型增加方法。
JavaScript沒有單獨的整數類型,我們可以通過Number.prototype添加一個integer來改善它。
Number.prototype.integer = function (){
return Math(this < 0 ? ‘ceiling‘ : ‘floor‘](this);
}
基本類型的原型是公共的結構,所以只在確定沒有改方法時才添加它。
4.8 遞歸
遞歸函數可以非常高效地操作樹形結構,比如瀏覽器端的文檔對象模型(DOM),每次遞歸調用時處理給定樹的一小段。
var walk_the_DOM = function walk(node,func){
func(node);
node = node.firstChild;
while(node){
walk(node,func);
node = node.nextSibling;
}
};
var getElementsByAttrbute = function(attr,value){
var rusults = [];
walk_the_DOM(document.body,function(node){
var actual = node.nodeType === 1 && node.getAttrbute(att);
if(typeof actual === ‘string‘ && (actual === value || typeof value !== ‘string‘)){
results.push(node);
}
});
return results;
};
一些語言提供了尾遞歸優化,這意味著如果一個函數返回自身遞歸調用的結果,那麽調用的過程會被替換為一個循環,它可以顯著提高速度。但這裏JavaScript沒有提供尾遞歸優化,深度遞歸的函數可能會因為返回堆棧溢出而運行失敗。
4.9 作用域
JavaScript有函數作用域,但是沒有塊級作用域。最好的做法是在函數整體的頂部聲明函數中可能用到的所有變量
4.10 閉包
內部函數擁有比它的外部更長的生命周期
一個常見的例子
var add_the_handlers = function (nodes) {
var i;
for(i=0; i < nodes.length; i += 1){
nodes[i].onclick = function (e){
alert(i);
}
}
};
它總是會顯示節點的數目,是因為事件處理器函數綁定了變量i,而不是函數在構造是的變量i的值
var add_the_handlers = function (nodes) {
var i;
for(i=0; i < nodes.length; i += 1){
nodes[i].onclick = function (i){
return function(e){
alert(i);
};
}(i);
}
};
4.11 回調
函數可以讓不連續事件的處理變得更容易,可以發起異步的請求,提供一個當服務器的響應到達時將被調用的回調函數。
4.12 模塊
通過使用閉包和函數來構造模塊,模塊是一個提供接口卻隱藏狀態與實現的函數或對象,通過使用函數去產生模塊,我們可以擯棄全局變量的使用,從而緩解這個糟糕的特性帶來的影響。
模塊的一般形式:一個定義了私有變量和函數的函數;利用閉包創建可以訪問私有變量和函數的特權函數;最後返回這個特權函數,或者把他們保存到一個可訪問到的地方。
模塊模式通常通過單例模式使用。
4.13 級聯
讓一些方法返回this而不是undefined,就可以啟用級聯。
在一個級聯中,我們可以在單獨一條的語句中依次調用同一個對象的很多方法。
4.14 套用
可以將函數與傳遞給它的參數相結合去產生出一個新的函數
4.15 記憶
在計算機領域中,記憶是主要用於加速程序計算的一種優化技術,是的函數避免重復演算之前已被處理的輸入,而返回以緩存的結果。
JavaScript的對象和數組要實現這種優化是非常方便的
var fibonacci = function (n) {
return n < 2 ? n : fibonacci (n - 1) + fibonacci (n - 2);
}
for (var i = 0; i <= 10; i += i){
document.writeIn( i + ‘=‘ + fibonacci(i));
}
這裏調用了11次,而它自身調用了442次去計算可能已被剛計算過的值。
var fibonacci = function(){
var memo = [0, 1];
var fib = function (n){
var result = memo[n];
if(typeof result !== ‘number‘) {
result = fib(n - 1) + fib(n -2);
memo[n] = result;
}
return result;
};
return fib;
}();
這個函數返回同樣的結果,調用了它11次,它自身調用了18次去取得之前存儲的結果。
JavaScript語言精粹_第四章