繼續上一篇文章《如何編寫高質量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 < y){
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 < y){
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< n;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 < n ;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
以上就是本文的全部內容了,希望大家能夠喜歡。