Javascript之 函式表示式
定義函式:
- 函式宣告:
function functionName(arg0, arg1, ... ){
// 函式體
} // name屬性訪問函式名
函式宣告提升:執行程式碼之前會先讀取函式宣告。所以即便函式宣告在呼叫函式程式碼之後,也不會報錯。
- 函式表示式:
var functionName = function(arg0, arg1, ... ){
// 函式體
}; //匿名函式
並沒有函式宣告提升,使用之前必須先賦值。
遞迴
一個函式通過名字呼叫自身的情況下稱為遞迴函式
問題:
function fnuc (num){
if (num <= 1){
return 1;
}
else{
return num*func(num - 1);
}
}
var anotherFunc = func;
func = null;
console.log(anotherFunc(10)); // error,因為func指向的已經變成null而不再是函式
解決辦法:
// 普通模式
function fnuc (num){
if (num <= 1){
return 1;
}
else{
return num*argeuments.callee (num - 1);
}
}
// 嚴格模式
var func = (function f (num){
if (num <= 1){
return 1;
}else{
return num * f(num - 1);
}
});
遞迴函式使用arguments.callee
來遞迴呼叫自身,不需要使用函式名。
閉包
有權訪問另一個函式作用域中的變數稱為閉包。
閉包是內部函式在其父函式已經終止之後可以引用其外部封閉函式中存在的變數的一種手段。
function Func (propertyName) {
return function (object_1, object_2) {
var value_1 = object_1(propertyName);
var value_2 = object_2(propertyName);
}
} // 即使內部函式被返回,而且在其他地方呼叫了,但他依然可以訪問變數property
// 原因是因為內部函式作用域鏈始終包含Func()的作用域
當某個函式被呼叫時,會建立一個執行環境及相應的作用域鏈,使用
argeuments
和其他命名引數
來初始化函式的活動物件。 但在作用域鏈中,外部函式活動物件始終處於第二位,外部函式的外部函式活動物件始終處在第三位,以此類推,知道作用域鏈的終點為全域性執行環境。
閉包與變數:
閉包的副作用:閉包只能取得包含函式中任何變數的最後一個值。
function arr () {
var result = new Array();
for (var i = 0; i < 10; i++){
result[i] = function(){
return i; // i的值一直為10
};
}
return result;
}
可以利用建立另一個匿名函式強制讓閉包符合預期:
function arr () {
var result = new Array();
for (var i = 0; i < 10; i++){
result[i] = function (num){
return function(){
return num;
};
}(i);
}
return result;
}
this物件:
匿名函式的執行環境具有全域性性,因此this物件
通常指向window
。
var name = "Dad";
var object = {
name: "Son",
getNameFunc: function (){
return function (){
return this.name;
};
}
};
console.log(objject.getNameFunc()()); // "Dad" 在非嚴格模式下
將外部作用域中的this
儲存在一個閉包能夠訪問的變數
裡,就能讓閉包訪問該變數:
var name = "Dad";
var object = {
name: "Son",
getNameFunc: function(){
var that = this;
return function(){
return that.name;
};
}
}
console.log(objject.getNameFunc()()); //"Son"
過度的使用閉包,會佔用大量的記憶體。
模仿塊級作用域
JavaScript沒有塊級作用域的概念,所以在塊語句中定義的變數,實際上是在包含函式中建立的而非語句中建立的。
function numbers (count) {
for (var i = 0; i < count; i++){
alert(i);
} // 一般擁有塊級作用域的語言,在迴圈結束後,便對迴圈內定義的變數進行銷燬。
var i; // 重新宣告變數,會被執行,但並不覆蓋原來的i
alert(i); // 輸出正常
}
解決辦法:利用匿名函式
模仿塊級作用域。
(function(){
// 這裡是塊級作用域
})(); // 後面的圓括號表示立即執行函式,其等價於:
/***********************
var func = function (){
// 塊級作用域
};
func();
***********************/
優點:可以減少閉包中佔有記憶體的問題,由於沒有指向匿名函式的引用,所以只要函式執行完畢就被銷燬。
私有變數
任何在函式中定義的變數,都認為是私有變數,因為不能在函式的外部訪問這些變數。
私有變數包括:函式的引數、區域性變數、在函式中定義的其他函式。
特權方法:有權訪問私有變數
和私有函式
的公有方法稱為特權方法。
// 利用建構函式定義特權方法
function MyObject () {
// 私有變數
var privateVariable = 10;
// 私有函式
function privateFunction () {
return false;
}
// 特權方法
this.publicMethod = function () {
privateVariable++;
return privateFunction();
};
}
建構函式模式針對每個例項都會創造一個新方法,利用靜態私有變數可以解決這個問題。
靜態私有變數
通過在私有作用域中定義私有變數和函式,建立特權方法。
(function(){ // 建立私有域
// 私有變數
var privateVariable = 10;
// 私有函式
function privateFunction(){
return false;
}
// 建構函式
MyObject = function(){ // 初始化未經宣告的變數,總是會建立全域性變數 [非嚴格模式下]
};
// 公有/特權方法
MyObject.prototype.publicMethod = function(){
privateVariable++;
return privateFunction();
};
})();
模組模式
為單例建立私有變數和特權方法。單例即只有一個例項的物件。
單例:
var obj = {
name: 'Xuchao',
method: function(){
// 方法程式碼
}
};
模組模式:
var oo = function(){
// 私有變數
var privateVariable = 10;
// 私有函式
function privateFunction(){
return false;
}
// 特權/公有方法和屬性
return { // 單例的公共介面
publicProperty: true;
publicMethod: function(){
privateVariable++;
return privateFunction();
}
};
}();
增強的模組模式
如果要返回一個特定模式的物件,則需要用到增強的模組模式。
var oo = function(){
// 私有變數
var privateVariable = 10;
// 私有函式
function privateFunction(){
return false;
}
// 建立物件
var object = new CustomType();
// 特權/公有方法和屬性
object.publicProperty = true;
object.publicMethod = function(){
privateVariable++;
return privateFunction();
};
return object; // 返回這個物件
}();