繼續上一篇文章《如何寫高品質JS程式碼》今次整理一下javascript函數知識點。
2.使用函數
函數為程式設計師提供了主要的抽像功能,又提供實作機制。函數可以獨立實作其他語言中的多個不同的特性,例如,過程、方法、建構函數,甚至類別或模組。
2.1 理解函數呼叫、方法呼叫以及建構子呼叫之間的不同
針對物件導向編程,函數、方法和類別的建構子是三種不同的概念。
使用模式:
1,函數呼叫
function hello(username){
return "hello" username;
}
2,方法呼叫
var obj = {
hello : function(){
return "hello , " this.username;
},
username : "floraLam"
};
ohj.hello();//"hello , floraLam"
this變數被綁定到物件是由於hello方法被定義在obj物件中,我們也可以子啊另外一個物件中賦值一份相同的函數引用,並且得到相同的答案。
var obj2 = {
hello : obj.hello(),
username : "floraLam"
};
3,建構子使用
function User(name,passwordHash){
this.name = name;
this.passwordHash = passwordHash;
}
使用new運算子來呼叫User則視為建構子。
var u = new User("floraLam","123");
與函數呼叫和方法呼叫不同的是,建構函式呼叫將一個全新的物件作為this變數的值,並隱式傳回這個新物件作為呼叫結果。建構函數的主要職責是初始化該新物件。
2.2 熟練高階函數
高階函數無非是那些將函數作為參數或返回值的函數,將函數作為參數(通常稱為回調函數,因為高階函數"隨後調用"它)是一種特別強大、富有表現力的慣用法,也在js程式中被大量使用。
考慮陣列的標準sort方法,為了對所有陣列都能運作,sort方法需要呼叫者決定如何比較陣列中的任兩個元素。
function compareNumber(x,y){
if(x
return -1;
}
if(x > y){
return 1;
}
return 0;
}
[3,1,4,1,5,9].sort(compareNumbers);//[1,1,3,4,5,9]
[3,1,4,1,5,9].sort(function(x,y){
if(x
return -1;
}
if(x > y){
return 1;
}
return 0;
});//[1,1,3,4,5,9]
上述例子使用一個匿名函數進一步簡化。
學會使用高階函數通常可以簡化程式碼並消除繁瑣的樣板程式碼。簡單的轉換字串陣列的操作我們可以使用循環這樣實現:
var names = ["Fred","Wilma","Pebbles"];
var upper = [];
for(var i = 0,n = names.length ;i
upper[i] = names[i].toUpperCase();
}
upper;//["FRED","WILMA","PEBBLES"];
使用陣列便利的map方法,可以消除循環,僅使用一個局部函數就可以對元素的逐個轉換。
var names = ["Fred","Wilma","Pebbles"];
var upper = names.map(function(name){
return name.toUpperCase();
});
upper;//["FRED","WILMA","PEBBLES"];
另外,例如我們想要建立若干個方法建立不同的字串,具有共同的實作邏輯,每個循環透過連接每個獨立部分的計算結果來建立一個字串。
function bulidString(n,callback){
var result = "";
for(var i = 0 ; i
result = callback(i);
}
return result;
}
var alphabet = bulidString(26,function(i){
return String.fromCharCode(aIndex i);
});
alphabet;//"abcdefghijklmnopqrxtuvwxyz";
var digits = buildString(10,function(i){ return i;})
digits;//"0123456789"
var random = buildString(9,function(){
random = String.fromCharCode(Math.floor(Math.random()*26) aIndex
});
random;//"yefjmcef"(隨機)
這樣能夠讓讀者更清楚地了解程式碼能做什麼,無須深入實作細節。
備註
javascript傳回指定範圍的隨機數(m-n之間)的公式:Math.random()*(n-m) m
同時要注意題目要求,是否要求回傳正整數
2.3呼叫模式
呼叫一個函數將會暫停目前函數的執行,傳遞控制權與參數給新的函數。 除了聲明時定義的形式參數,每個函數會接收到兩個新的附加參數:this和arguments。
this是個很重要的參數,而且它的值是由呼叫模式決定的。
以下是JavaScript中很重要的4個呼叫模式:
a. 方法呼叫模式the method invocation pattern
b. 函數呼叫模式the function invocation pattern
c. 構造器呼叫模式the constructor invocation pattern
d. Apply呼叫模式the apply invocation pattern
這些模式在如何初始化關鍵參數this上存在差異
1. 方法呼叫模式the method invocation method
當函數作為物件的方法的時候,我們就叫函數為方法。當一個方法被呼叫的時候,this綁定到呼叫的物件。
var myObj={
val:0,
increment:function(inc){
this.val =typeof inc ==="number"? inc:1;
},
get_val:function(){return this.val;}
}
myObj.increment();// 1
myObj["increment"](2);//3
小結:
1、透過this可取得它們所屬物件的上下文的方法稱為公共方法
2、當用 .或是下標表達式 來使用一個函數的時候,就是方法呼叫模式,this物件綁定到前面的物件。
3,一個函數可以使用this來存取對象,所以它能檢索對象的值或是改變對象的值。綁定this到物件發生在呼叫的時候。
2. 函數呼叫模式the function invocation pattern
當一個函數不是一個物件的屬性,那麼它就是作為函數來呼叫的。當一個函數作為函數呼叫模式來呼叫的時候,this綁定到全域物件。這是JavaScript設計時的錯誤並且延續了下來。
function add(x,y){
return x y;
}
myObj.double=function(){
var that=this;
var helper=function(){
that.val=add(that.value,that.value);
//錯誤的寫法可能是這樣,為什麼錯呢?因為函數作為內部函數呼叫的時候,this已經綁定到了錯誤的對象,全局對象並沒有val屬性,所以返回不正確的值。
//this.val = this.val this.val;
}
helper();
}
myObj.double();//6
3. 構造器呼叫模式the constructor invocation pattern
JavaScript是一門基於原型繼承的語言,這意味著物件可以直接繼承屬性從其它的對象,該語言是無類別的。
如果在一個函數前面帶上new來調用,那麼將會得到一個隱藏連接到該函數的prototype成員的新對象,同時this也會綁定到該新對象。
new前綴也會改變return語句的行為。這也不是推薦的程式設計方式。
var Foo = function(status){
this.status = status;
}
Foo.prototype.get_status = function(){
return this.status;
}
//建構一個Foo實例
var myFoo = new Foo("bar");
myFoo.get_status();//"bar"
4. Apply呼叫模式the apply invocation pattern
因為JavaScript是一個函數式的物件導向語言,所以函數可以擁有方法。
Apply方法擁有兩個參數,第一個是將綁定到this的值,第二個是參數數組,也就是說Apply方法讓我們建立一個數組並用其去調用函數,即允許我們選擇this的值,也允許我們選擇數組的值。
var array = [3,4];
var sum = add.apply(null,array); // 7
var statusObj = {status:"ABCDEFG"};
Foo.prototype.pro_get_status = function(prefix){
return prefix "-" this.status;
}
var status = Foo.prototype.get_status.apply(statusObj);// "ABCDEFG"
var pro_status = Foo.prototype.get_status.apply(statusObj,["prefix"]);// "prefix -ABCDEFG"
通常情況下,函數或方法的接收者(級綁定到特殊關鍵字this的值)是由呼叫者的語法決定性的。特別地,方法呼叫語法將方法被查找物件綁定到this變數。然而,有時需要使用自訂接收者來呼叫函數。這時候就需要使用call方法或bind方法自訂接收者來呼叫方法
2.4 使用bind方法提取具有確定接受者的方法
由於方法與值為函數的屬性沒有區別,因此也容易提取物件的方法並提取函數作為回呼函數直接傳遞給高階函數。
但這也很容易忘記將提取出來的函數的接受著綁定到該函數被提取出的物件上。
var buffer = {
entries: [],
add :function(s){
this.entries.push(s);
}
}
var source = ["867","-","5309"];
source.forEach(butter.add);//error:entries is undefined
這時候butter.add的接受者不是butter物件。函數的接收者取決於它是如何被調用的,forEach方法在全域作用域中被調用,因此forEach方法的實作使用全域物件作為預設的接收者,由於全域物件中沒有entries屬性,因此這段程式碼拋出錯誤。
forEach方法允許呼叫者提供一個可選的參數作為回呼函數的接收者。
var source = ["867","-","5309"];
source.forEach(butter.add,butter);
但並非所有高階函數都細心周到為使用者提供回呼函數的接收者。
解決方法有兩種:
1)建立一個明確地一buffer物件方法的方式呼叫add的封裝函數。不管封裝函數如何被調用,它總是能確保將其參數推送到目標數組中。
var source = ["867","-","5309"];
source.forEach(function(s){
butter.add(s);
});
2)函數物件的bind方法需要一個接收者對象,並產生一個以該接收者物件的方法呼叫的方法呼叫原來的函數的封裝函數。
var source = ["867","-","5309"];
source.forEach(butter.add.bind(buffer));
備註
buffer.add.bind(buffer)建立一個新函數而不是修改buffer.add函數:
buffer.add === buffer.add.bind(buffer); //false
以上就是本文的全部內容了,希望大家能夠喜歡。