一、函數聲明;二、函數表達式java
函數聲明的重要特徵:函數聲明提高,在執行代碼以前會先讀取函數聲明。c++
sayHi();
function sayHi(){
console.log("Hi");
}
複製代碼
函數表達式必須先賦值。數組
function fact(num){
if(num <= 1){
return 1;
}else{
return num*fact(num-1);
}
}
複製代碼
下面代碼可能致使出錯bash
var another = fact;
fact = null;
console.log(another(4)); //出錯
複製代碼
使用arguments.callee能夠解決這個問題,arguments.callee是一個指向正在執行的函數的指針。閉包
function fact(num){
if(num<=1){
return 1;
}else{
return num*arguments.callee(num-1);
}
}
複製代碼
在嚴格模式下,不能經過arguments.callee訪問,建立一個名爲f()的函數,將它賦值給fact。函數
var fact = (function f(num){
if(num <= 1){
return 1;
}else{
return num * f(num-1);
}
});
複製代碼
閉包是指有權訪問另外一個函數做用域中的變量的函數。當某個函數被調用時,會建立一個執行環境及相應的做用域鏈。而後,使用arguments和其餘命名參數的值來初始化函數的活動對象。ui
通常來講,當函數執行完畢後,局部活動對象就會被銷燬,內存中僅保存全局做用域(全局執行環境的變量對象)。可是,閉包的狀況有所不一樣。this
function create(name){
return function(){
return name;
}
}
複製代碼
create()函數在執行完畢後,其活動對象不會被銷燬,由於匿名函數的做用域鏈仍然在引用這個活動對象。當create()函數返回後,其執行環境的做用域鏈會被銷燬,但它的活動對象仍然會留在內存中;直到匿名函數被銷燬後,create()活動對象纔會被銷燬。spa
一、閉包和變量指針
做用域鏈的這種配置,使閉包只能取得包含函數中任何變量的最後一個值。閉包保存的是整個變量對象,而不是某個特殊的變量。
function create(){
var result = new Array();
for(var i =0;i<10;i++){
result[i] = function(){
return i;
};
}
return result;
}
複製代碼
數組10個10,每一個函數的做用域都保存着create()函數的活動對象,因此引用的都是同一個變量i。
建立一個匿名函數強制讓閉包的行爲符合預期:
function create(){
var result = new Array();
for(var i=0;i<10;i++){
result[i] = function(num){
return function(){
return num;
};
}(i);
}
return result;
}
複製代碼
這個函數中,沒有直接把閉包賦值給數組,而是定義了一個匿名函數,並將當即執行該匿名函數的結果賦給數組。這裏的匿名函數有一個參數num,也就是最終函數要返回的值。在調用每一個匿名函數時,咱們傳入了變量i。因爲函數參數是按值傳遞的,因此就會將變量i的當前值複製給參數num。而在這個匿名函數內部,又建立並返回一個訪問num的閉包。這樣一來,result數組中的每一個函數都有num變量的一個副本,所以就能夠返回各自不一樣的數值了。
二、this對象
this對象是在運行時基於函數的執行環境綁定的:在全局函數中,this等於window,函數被看成某個對象的方法調用時,this等於那個對象。匿名函數的執行環境具備全局性,所以this一般指向window。
構造函數看成普通函數調用時,this表明的是全局window對象。和new使用建立對象,指向當前的對象。
var name = 'this window';
var object = {
name: 'my object',
getName: function(){
return function(){
return this.name;
};
}
};
alert(object.getName()()); //"this window"
複製代碼
因爲getName()返回一個函數,所以調用object.getName()()就會當即調用它返回的函數,結果就是返回一個字符串。
三、內存泄漏
function assign(){
var element = document.getElementById("someElement");
element.onclick = function(){
alert(element.id);
};
}
複製代碼
以上代碼建立一個做爲element元素事件處理程序的閉包,而這個閉包又建立了一個循環引用。因爲匿名函數保存了一個對assign()活動對象的引用,所以會致使沒法減小element的引用數。只要匿名函數存在,element的引用數至少也是1,所以它佔用的內存就永遠不會被回收。
可經過如下解決:
function assign(){
var element = document.getElementById("someElement");
var id = element.id;
element.onclick = function(){
alert(id);
};
element = null;
}
複製代碼
閉包會引用包含函數的整個活動對象,而其中包含着element。即便不直接引用element,包含函數的活動對象中也仍然會保存一個引用。
JS沒有塊級做用域的概念。在塊語句中定義的變量,實際是在包含函數中而非語句中建立的。
function output(count){
for(var i=0;i<count;i++){
alert(i);
}
var i; //從新聲明變量,視而不見
alert(i);
}
複製代碼
在java、c++語言中,變量i只會在for循環的語句中有定義,循環一旦結束,變量i就會被銷燬。在JS中,變量i定義在output()的活動對象中,從它又定義開始就能夠在函數內部訪問它。
用做塊級做用域的(私有做用域)的匿名函數的語法:
(function(){
//這裏是塊級做用域
})();
複製代碼
重寫output()函數:
function output(count){
(function(){
for(var i=0;i<count;i++){
alert(i);
}
})();
alert(i); //致使一個錯誤
}
複製代碼
5、私有變量