在之前的JavaScript學習中,this,call,apply總是讓我感到迷惑,但是他們的運用又非常的廣泛。遂專門花了一天,來弄清楚JavaScript的this,call,apply。
中途參考的書籍也很多,以《JavaScript設計模式與開發實踐》為主,《JavaScript高級程式設計》、《你不知道的JavaScript》為輔。這三本書對我理解this,call,apply都起了很大的幫助。
首先,我們先講述this。
在《JavaScript設計模式與開發實踐》關於this的描述中,我認為有一句話切中了this的核心要點。那就是:
JavaScript的this總是指向一個物件
具體到實際應用中,this的指向又可以分為以下四種:
作為物件的方法呼叫
作為普通函數呼叫
構造器呼叫
構造器呼叫
構造器呼叫
構造器呼叫
/** * 1.作为对象的方法调用 * * 作为对象方法调用时,this指向该对象。 */ var obj = { a: 1, getA: function() { console.log(this === obj); console.log(this.a); } }; obj.getA(); // true , 1
1.作為物件的方法呼叫
說明:作為物件方法呼叫時,this指向該物件。
/** * 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'
2.當普通函數呼叫
說明:當以普通函數呼叫時,this總是指向全域物件(瀏覽器中是window)。
/** * 3.作为构造器调用 * * 作为构造器调用时,this指向返回的这个对象。 */ var myClass = function() { this.name = "Lxxyx"; }; var obj = new myClass(); console.log(obj.name); // Lxxyx console.log(obj) // myClass {name: "Lxxyx"}
var myClass = function() { this.name = "Lxxyx"; // 加入return时,则返回的是别的对象。this不起作用。 return { name:"ReturnOthers" } }; var obj = new myClass(); console.log(obj.name); // ReturnOthers
舉例:
但是如果構造函數中手動指定了return其它對象,那麼this將不起作用。如果return的是別的資料類型,就沒有問題。
Call和Apply的用途一樣。都是用來指定函數體內this的指向。
Call和Apply的區別
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指向objCall:第一個參數為this的指向,要傳給函數的參數得一個一個的輸入。
(function(a, b) { console.log(arguments) // 1,2 // 调用Array的原型方法 Array.prototype.push.call(arguments, 3); console.log(arguments) // 1,2,3 })(1,2)說明:這是call和apply最常使用的用途了。用於改變函數體內this的指向。
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; }
函數具有arguments屬性,而arguments是一個類別數組。 但是arguments是不能直接呼叫數組的方法的,所以我們要用call或apply來呼叫Array物件的原型方法。 原理也很容易理解,例如剛才呼叫的是push方法,而push方法在谷歌的v8引擎中,原始碼是這樣的:
它只與this有關,所以只要是類別數組對象,都可以呼叫相關方法去處理。 這部分內容比較複雜,再加上自己水平也不太夠。所以推薦有條件的同學去買相關書籍,或是等我的後續部落格文章。感想
透過對這部分的學習,算是加深了對JavaScript的理解。最直覺的表現就是,去看一些優秀框架的原始碼時,不再是被this,call,apply,bind繞的暈乎乎的。還是很開心的~
下一段時間,準備深入探索日常學習和使用的CSS。畢竟JavaScript學了,HTML和CSS也不能落下。