天天看點

JS學習筆記3_函數表達式

1.函數表達式與函數聲明的差別

函數聲明有“提升”(hoisting)的特性,而函數表達式沒有。也就是說,函數聲明會在加載代碼時被預先加載到context中,而函數表達式隻有在執行表達式語句時才會被加載

2.閉包

有權通路另一個函數作用域中的變量的函數。閉包可以通路另一個作用域中的變量,是以閉包得到的變量值是最終值,而不是該變量在某一時刻的值,有一個很經典的例子:

function createFuns(){
var result = new Array();
for(var i = 0;i < 10;i++){
result[i] = function(){
return i;
};
 
/*
result[i] = function(arg){
return function(){
return arg;
}
}(i);//此處匿名函數立即執行的()可以省略,因為function在等号右邊出現,不存在歧義(一般形式是(function(){})())
*/
}
 
return result;
}      

createFuns函數傳回一組函數,這些函數執行結果都是10(閉包得到的變量值是最終值),但注釋中的方式傳回函數的執行結果就是i的目前值,因為是值傳遞

3.函數表達式中的this

内部函數無法直接通路外部函數中的this和arguments對象,因為内部函數在搜尋這兩個變量時,隻會搜尋到其活動對象為止

P.S.活動對象是作用域鍊的實體,作用域鍊是抽象概念,而活動對象就是這個概念的具體實作。其實作用域鍊映射到代碼裡就是變量對象鍊條,又扯出了變量對象,不用怕,一點都不複雜:

執行環境(context)中定義的所有變量和函數都儲存在變量對象中,如果執行環境是一個函數,那就把函數的活動對象當作變量對象,并作為變量對象鍊的一環,也就是作用域鍊的一環。

一開始函數的活動對象隻有一個屬性——arguments對象,每在函數内部聲明一個自定義屬性,就給該函數的活動對象添加一個屬性。。。

嗯,扯了這麼多,其實就一句話:this就是活動對象/變量對象的引用。

如果還不大明白,請參考前輩的博文,順便推薦這位前輩的其它博文,關于js的都很不錯

4.重複聲明變量

不會引起文法錯誤,會自動忽略多餘的聲明,但聲明同時的初始化操作會被執行,例如:

1

2

3

​var​

​​ ​

​x = 1;​

​var​

​​ ​

​x = 2;​

​​

​//var聲明被忽略,是以不報錯,但指派會被執行​

​alert(x);​

​​

​//2​

5.實作塊級作用域的思路

聲明一個匿名函數并立即調用,那麼匿名函數内部就是塊級作用域。具體實作:

1

​(​

​​

​function​

​​

​(){​

​​

​/*塊級作用域*/​

​​

​})();​

注意:需要用圓括号來消除函數表達式與函數聲明的歧義(從解釋器的角度看就是這樣),因為函數聲明後面不能直接跟圓括号,而函數表達式可以。

P.S.示例中的代碼隻是一種實作IIFE的方式,還有幾種,具體請參考[javascript]IIFE立即執行的函數表達式,這篇博文給出了詳細的對比

6.私有變量

函數内部用var或者function聲明的變量是私有變量。(執行個體無法直接通路,可以通過定義公有函數來提供通路接口)

而用this.attr = value;方式聲明的變量是公有變量。(執行個體可以直接通路)

7.閉包、匿名函數、内部函數、内部匿名函數的差別

  • 閉包:有權通路另一個函數作用域中的變量的函數
  • 匿名函數:沒有名字的函數表達式
  • 内部函數:在函數内部聲明的函數,也就是閉包
  • 内部匿名函數:在函數内部聲明的匿名函數,當然,也是閉包

P.S.濫用閉包可能會占用大量記憶體,閉包之是以能夠通路外層函數作用域中的變量,是因為閉包的活動對象持有外層函數活動對象的引用

隻有在閉包被銷毀後,外層函數中的變量才能被銷毀,不能及時銷毀,是以可能占用大量記憶體

8.執行函數的整個過程

  1. 建立執行環境context,context有内部屬性[[Scope]]
  2. 更新作用域鍊ScopeChain
  3. 建立活動對象
  4. 初始化活動對象的this, arguments, 形參等各個屬性
  5. 執行函數體
  6. 銷毀活動對象(存在閉包時無法銷毀)
  7. 更新作用域鍊

9.js單例模式與子產品模式

  • 單例模式:建立隻有一個執行個體的對象的一種模式,說白了,模式就是方法,設計模式就是前輩總結出來的好方法。js中實作單例模式尤其簡單:
var singleton = {attr1: value1, attr2: value2};      

沒錯,就是對象字面量,其實就是建立了匿名對象,不知道構造函數的名字,當然無法建立第2個執行個體了

  • 子產品模式:道格拉斯提出的用來增強單例的方法,可以給單例添加私有屬性和公有屬性(有時候也叫特權屬性,表示有權修改私有屬性的屬性),例如:
var singleton = function(){
//私有屬性
var privateStr = 'secret';
function addPrefix(){
privateStr = 'this is my ' + privateStr;
}
 
//公有屬性(特權屬性)
return{//傳回一個匿名對象
getStr : function(){
addPrefix();
return privateStr;
}
};
}();//又是匿名函數立即執行
 
alert(singleton.getStr());      

P.S.如果不需要單例,隻要保護私有屬性的話,可以這樣做:

function Cat(){
//私有屬性
var privateStr = 'secret';
function addPrefix(){
privateStr = 'this is my ' + privateStr;
}
 
//公有屬性(特權屬性)
this.getStr = function(){
addPrefix();
return privateStr;
}
}
var obj = new Cat();
alert(obj.getStr());      

參考資料

  • this到底是個啥東西:前輩的博文
  • 匿名函數立即執行的幾種方式:[javascript]IIFE立即執行的函數表達式