在之前的JavaScript學習中,this,call,apply總是讓我感到迷惑,但是他們的運用又非常的廣泛。遂專門花了一天,來弄懂JavaScript的this,call,apply。
中途參考的書籍也很多,以《JavaScript設計模式與開發實踐》爲主,《JavaScript高級程序設計》、《你不知道的JavaScript》爲輔。這三本書對我理解this,call,apply都起了很大的幫助。
this
首先,我們先講述this。
在《JavaScript設計模式與開發實踐》關於this的描述中,我認爲有一句話切中了this的核心要點。那就是:
JavaScript的this總是指向一個對象
具體到實際應用中,this的指向又可以分爲以下四種:
- 作爲對象的方法調用
- 作爲普通函數調用
- 構造器調用
- apply和call調用
接下來我們去剖析前3點,至於第4點的apply和call調用,會在call和apply部分詳細講解。
1.作爲對象的方法調用
說明:作爲對象方法調用時,this指向該對象。
舉例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
/** * 1.作爲對象的方法調用 * * 作爲對象方法調用時,this指向該對象。 */ var
obj = { a: 1, getA:
function () { console.log( this
=== obj); console.log( this .a); } }; obj.getA();
// true , 1 |
2.作爲普通函數調用
說明:作爲普通函數調用時,this總是指向全局對象(瀏覽器中是window)。
舉例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
/** * 2.作爲普通函數調用 * * 不作爲對象屬性調用時,this必須指向一個對象。那就是全局對象。 */ window.name =
'globalName' ; var
getName = function () { console.log( this .name); }; getName();
// 'globalName' var
myObject = { name:
"ObjectName" , getName:
function () { console.log( this .name) } }; myObject.getName();
// 'ObjectName' // 這裏實質上是把function() {console.log(this.name)} // 這句話賦值給了theName。thisName在全局對象中調用,自然讀取的是全局對象的name值 var
theName = myObject.getName; theName();
// 'globalName' |
3.構造器調用
說明:作爲構造器調用時,this指向返回的這個對象。
舉例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/** * 3.作爲構造器調用 *
* 作爲構造器調用時,this指向返回的這個對象。 */ var
myClass = function () { this .name =
"Lxxyx" ; }; var
obj = new myClass(); console.log(obj.name);
// Lxxyx console.log(obj)
// myClass {name: "Lxxyx"} |
但是如果構造函數中手動指定了return其它對象,那麼this將不起作用。
如果return的是別的數據類型,則沒有問題。
1
2
3
4
5
6
7
8
9
10
|
var
myClass = function () { this .name =
"Lxxyx" ; // 加入return時,則返回的是別的對象。this不起作用。 return
{ name: "ReturnOthers" } }; var
obj = new myClass(); console.log(obj.name);
// ReturnOthers |
4.Call和Apply
Call和Apply的用途一樣。都是用來指定函數體內this的指向。
Call和Apply的區別
Call:第一個參數爲this的指向,要傳給函數的參數得一個一個的輸入。
Apply:第一個參數爲this的指向,第二個參數爲數組,一次性把所有參數傳入。
如果第一個參數爲null,則this指向調用的本身。
1.改變this指向
說明:這是call和apply最常用的用途了。用於改變函數體內this的指向。
舉例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
var
name = "GlobalName" var
func = function () { console.log( this .name) }; func(); // "GlobalName" var
obj = { name:
"Lxxyx" , getName:
function () { console.log( this .name) } }; obj.getName.apply(window)
// "GlobalName" 將this指向window func.apply(obj)
// "Lxxyx" 將this指向obj |
2.借用其它對象的方法
這兒,我們先以一個立即執行匿名函數做開頭:
1
2
3
4
5
6
|
( function (a, b) { console.log(arguments)
// 1,2 // 調用Array的原型方法 Array.prototype.push.call(arguments, 3); console.log(arguments)
// 1,2,3 })(1,2) |
函數具有arguments屬性,而arguments是一個類數組。
但是arguments是不能直接調用數組的方法的,所以我們要用call或者apply來調用Array對象的原型方法。
原理也很容易理解,比如剛纔調用的是push方法,而push方法在谷歌的v8引擎中,源代碼是這樣的:
1
2
3
4
5
6
7
8
9
|
function
ArrayPush() { var
n = TO_UINT32( this .length);
// 被push對象的長度 var
m = % _ArgumentsLength(); // push的參數個數 for
( var
i = 0; i < m; i++) { this [i + n] = % _Arguments(i);
// 複製元素 } this .length = n + m;
//修正length屬性 return
this .length; } |
它只與this有關,所以只要是類數組對象,都可以調用相關方法去處理。
這部分內容比較複雜,再加上自己水平也不太夠。所以推薦有條件的同學去購買相關書籍,或者等我的後續博客文章。
轉載於:hebedich