js函式:閉包難點+this的理解
1.函式的定義
1.語法
function functionName(parameters) {
執行的程式碼
}
2.可以通過通過一個表示式定義,函式表示式可以儲存在變數中
var x = function (a, b) {return a * b};
var z = x(4, 3); //這種函式實際上是一個匿名函式
3.函式的提升
myFunction(5); //先呼叫
function myFunction(y) { //後定義
return y * y;
}
4.自呼叫函式:如果表示式後面緊跟 () ,則會自動呼叫。
(function () {
var x = "Hello!!"; // 我將呼叫自己
})();
5.函式可作為一個值、一個表示式使用
function myFunction(a, b) {
return a * b;
}
var x = myFunction(4, 3);
var x = myFunction(4, 3) * 2;
6.函式的資料型別typeof是一個物件,有屬性和方法。
2.引數
1.顯式引數
函式顯式引數在函式定義時列出,定義顯式引數時沒有指定資料型別。
2.隱式引數
函式隱式引數在函式呼叫時傳遞給函式真正的值。
我認為就是形參與實參
3.arguments 物件
- 在函式程式碼中,使用特殊物件
arguments[]
,開發者無需明確指出引數名,就能訪問它們。 舉例: 用arguments[0]可以訪問第一個引數的值,相當於陣列 - 引用屬性
arguments.length
,可以檢測函式的引數個數 - 模擬函式過載
function doAdd() { if(arguments.length == 1) { alert(arguments[0] + 5); } else if(arguments.length == 2) { alert(arguments[0] + arguments[1]); } } doAdd(10); //輸出 "15" doAdd(40, 20); //輸出 "60"
3.呼叫,關於this
JavaScript 函式有 4 種呼叫方式,每種方式的不同在於 this 的初始化,this指向函式執行時的當前物件。
- 作為函式呼叫:myFunction() 和 window.myFunction() 是一樣的,this指向全域性物件,web中的全域性物件是瀏覽器視窗windows。
- 作為方法呼叫:this指向的物件是myObject
var myObject = { firstName:"John", lastName: "Doe", fullName: function () { return this.firstName + " " + this.lastName; } } myObject.fullName(); // 返回 "John Doe
var myObject = {
firstName:"John",
lastName: "Doe",
fullName: function () {
return this;
}
}
myObject.fullName(); // 返回 [object Object] (所有者物件)
//函式作為物件方法呼叫,會使得 this 的值成為物件本身
[object Object] 自定義的物件
第一個object代表使用者自定義的物件的屬性,
第二個Object代表使用者自定義的物件的方法。
3.使用建構函式呼叫函式
如果函式呼叫前使用了 new 關鍵字, 則是呼叫了建構函式。這看起來就像建立了新的函式,但實際上 JavaScript 函式是重新建立的物件:
// 建構函式:
function myFunction(arg1, arg2) {
this.firstName = arg1;
this.lastName = arg2;
}
// This creates a new object
var x = new myFunction("John","Doe");
x.firstName; // 返回 "John"
//建構函式的呼叫會建立一個新的物件。新物件會繼承建構函式的屬性和方法。
建構函式中 this 關鍵字沒有任何的值。this 的值在函式呼叫例項化物件(new object)時建立。this指向新的物件。
4.作為函式方法呼叫函式
JavaScript 函式是物件,有方法和屬性。方法有call() 和 apply()
- apply()方法 :接收兩個引數,第一個是函式執行的作用域(this),另一個是引數陣列。
語法:apply([thisObj [,argArray] ]);
- call()方法 :第一個引數和apply()方法的一樣,第一個時作用域this,但是傳遞給函式的引數必須列舉出來。
語法:call([thisObject[,arg1 [,arg2 [,...,argn]]]]);
舉例:
function myFunction(a, b) { return a * b; } myArray = [10, 2]; myObject = myFunction.apply(myObject, myArray); // 返回 20
5. 因為巢狀的原因,巢狀函式裡的this指向windows
總結:
1、 誰呼叫了此函式, 那麼this 就指向誰
2、如果遇到函式裡面又包了一層函式,那麼 this的指向就變成全域性的了
3、 如果遇到new 那麼指向就是 新建立的例項物件
4、如果遇到call 或apply,那麼 this 指向就是 call或apply 裡面的第一個引數
4.閉包
1.作用域:函式內部可以直接讀取全域性變數,在函式外部自然無法讀取函式內的區域性變數。函式內部宣告變數的時候,一定要使用var命令。如果不用的話,實際上聲明瞭一個全域性變數!
2.如何從外部讀取區域性變數?
方法:在函式的內部,再定義一個函式。
function f1(){
var n=999;
function f2(){
alert(n); // 999
}
}
在上面的程式碼中,函式f2就被包括在函式f1內部,這時f1內部的所有區域性變數,對f2都是可見的。但是反過來就不行,f2內部的區域性變數,對f1就是不可見的。所以,父物件的所有變數,對子物件都是可見的,反之則不成立。這就是Javascript語言特有的"鏈式作用域"結構(chain scope),子物件會一級一級地向上尋找所有父物件的變數。
3.閉包:
既然f2可以讀取f1中的區域性變數,那麼只要把f2作為返回值,我們不就可以在f1外部讀取它的內部變量了嗎!f1中一定要定義區域性變數啊!
function f1(){
var n=999;
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
閉包就是能夠讀取其他函式內部變數的函式。
4.閉包的作用
一個是前面提到的可以讀取函式內部的變數,另一個就是讓這些變數的值始終保持在記憶體中。所以閉包很佔記憶體!
function f1(){
var n=999; //f1要用var定義,才是區域性變數!
nAdd=function(){n+=1} //注意:nAdd是一個全域性變數
function f2(){
alert(n);
}
return f2;
}
var result=f1(); //result實際就是閉包f2函式
result(); // 999
nAdd(); //nAdd的值是一個匿名函式),而這個匿名函式本身也是一個閉包
result(); // 1000
第一次的值是999,第二次的值是1000。這證明了,函式f1中的區域性變數n一直儲存在記憶體中,並沒有在f1呼叫後被自動清除。原因就在於f1是f2的父函式,而f2被賦給了一個全域性變數,這導致f2始終在記憶體中,而f2的存在依賴於f1,因此f1也始終在記憶體中,不會在呼叫結束後,被垃圾回收機制(garbage collection)回收。
5.閉包與this
程式碼一:
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()());
返回值是The Window,外面的函式指向object,當函式裡套一個函式,此時this發生了變化, 指向了全域性 window
程式碼二閉包:
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()());
返回值是My Object。
作為方法呼叫,哪個物件呼叫this就指向哪個物件。
這是一個閉包。