學習起因:
在之前的JavaScript學習中,this,call,apply總是讓我感到迷惑,但是他們的運用又非常的廣泛。遂專門花了一天,來弄懂JavaScript的this,call,apply。
中途參考的書籍也很多,以《JavaScript設計模式與開發實踐》為主,《JavaScript高級程序設計》、《你不知道的JavaScript》為輔。這三本書對我理解this,call,apply都起了很大的幫助。
this
首先,我們先講述this。
在《JavaScript設計模式與開發實踐》關於this的描述中,我認為有一句話切中了this的核心要點。那就是:
JavaScript的this總是指向一個對象
具體到實際應用中,this的指向又可以分為以下四種:
接下來我們去剖析前3點,至於第4點的apply和call調用,會在call和apply部分詳細講解。
1.作為對象的方法調用
說明:作為對象方法調用時,this指向該對象。
舉例:
/**
* 1.作為對象的方法調用
*
* 作為對象方法調用時,this指向該對象。
*/
var obj = {
a: 1,
getA: function() {
console.log(this === obj);
console.log(this.a);
}
};
obj.getA(); // true , 1
2.作為普通函數調用
說明:作為普通函數調用時,this總是指向全局對象(浏覽器中是window)。
舉例:
/**
* 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指向返回的這個對象。
舉例:
/**
* 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的是別的數據類型,則沒有問題。
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的指向。
舉例:
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.借用其它對象的方法
這兒,我們先以一個立即執行匿名函數做開頭:
(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引擎中,源代碼是這樣的:
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有關,所以只要是類數組對象,都可以調用相關方法去處理。
這部分內容比較復雜,再加上自己水平也不太夠。所以推薦有條件的同學去購買相關書籍,或者等我的後續博客文章。
感想
通過對這部分的學習,算是加深了對JavaScript的理解。最直觀的表現就是,去看一些優秀框架的源代碼時,不再是被this,call,apply,bind繞的暈乎乎的。還是很開心的~
下一段時間,准備深入探索一下日常學習和使用的CSS。畢竟JavaScript學了,HTML和CSS也不能落下。