摘要:第二種和第三種分別為級事件和級事件,其實質是給點擊事件指定了一個回調函數,其為。也是和實例對象嚴格相等的,可以說明,構造函數中的是指代實例對象。參考鏈接關鍵字深入理解上下文在線轉化構造函數原文發表在我的博客關鍵字,歡迎訪問
涵義
this關鍵字是一個非常重要的語法點。毫不夸張地說,不理解它的含義,大部分開發任務都無法完成。
首先,this總是返回一個對象,簡單說,就是返回屬性或方法“當前執行環境”的對象。
var showName = function() { console.log("My name is", this.name); }; var zs = { name: "Zhang San", describe: showName }, ls = { name: "Li Si", describe: showName }; zs.describe(); // My name is Zhang San ls.describe(); // My name is Li Si showName(); // My name is name = "window"; // 等價于 window.name = "window"; 和this.name = "window"; 因為此時window===this showName(); // My name is window
上面代碼中定義了showName方法,將在控制臺輸出"My name is "并拼接上this.name,并將這個方法賦給了zs和li這兩個對象的describe方法。
當通過這兩個對象調用describe方法時,分別輸出zs和ls的name屬性。
直接在全局環境下調用showName方法并沒有報錯,但也沒有輸出任何內容。不報錯的原因是此時的this指的是瀏覽器的window對象,window對象有name屬性。沒有輸出內容的原因是window.name初始值是一個空字符串。
我們給name屬性賦值為"window"后再次執行showName方法時,將輸出: My name is window
以上示例中實際都是執行的showName方法,但是由于環境不同,輸出的結果也不同,根本原因是不同情況下的this是不一樣的。
zs.describe(); this === zs
ls.describe(); this === ls
showName(); this === window
再看一個例子:
點擊三個按鈕,控制臺輸出結果分別是什么呢?
第一個為:My name is window 第二個為:My name is 按鈕2 ,第三個為My name is 按鈕3
這是為什么呢?這個和綁定事件的機制有關系。第一種形式是HTML事件,onclick="showName()"表示在點擊時執行showName方法,此時執行環境為全局環境,this為window,所以輸出window。
第二種和第三種分別為DOM0級事件和DOM2級事件,其實質是給點擊事件指定了一個回調函數,其為showName。在點擊事件的回調函數中,this是指當前這個dom元素,因此輸出的值為這兩個按鈕的name屬性。
使用場合this的使用是很廣泛的,其作用也非常強大。我們可以將this的使用歸為一下幾類。
構造函數在構造函數中,this的出現頻率是非常高的,它指的是實例對象。
function Person(name, gender) { this.name = name; this.gender = gender; } zs = new Person( "zs" , "male" ); // { // name: "zs", // gender: "male" // }
上面使用構造函數產生實例對象時,兩個參數賦值給了實例對象就是通過this來完成的。
function Person(name, gender) { this.name = name; this.gender = gender; } Person.prototype.showSelf = function() { return this; } zs = new Person("zs", "male"); // {name: "zs", gender: "male"} zs.showSelf(); // {name: "zs", gender: "male"} zs === zs.showSelf(); // true
上面代碼通過showSelf方法返回了構造函數里的this,它的輸出內容和實例對象一致。也是和實例對象嚴格相等的,可以說明,構造函數中的this是指代實例對象。
對象的方法在對象里面定義的方法中也經常看到this的身影,那么此時的this指的又是什么呢?
大多數情況下,this指的是當前的這個對象,比如:
var box = { id: +new Date(), name: "noName", setName: function(name) { this.name = name; }, getName:function(){ return this.name; } } box.getName(); // "noName" box.setName("box1"); box; // {id: 1476846238291, name: "box1"}
上面代碼中在通過box對象來調用setName和getName方法的情況下,this指的就是當前的這個對象,此處為box,也正是由于這種情況下的this指的是當前對象,我們才能通過這兩個方法對box的name屬性進行讀寫。
但是只有box.getName() 和box.setName("box1")這樣使用時,this才指向當前對象。請看下面例子:
當將一個對象的一個方法賦給另一個對象時,this的指向也會改變。
var box = { name: "box", getName: function() { return this.name; } }; var bag = { name: "bag" }; bag.getName = box.getName; bag.getName(); // "bag"
雖然bag.getName 實際是對box.getName 的一個引用,由于運行時使用的是bag.getName(),此時是在bag對象下運行的,this也就指的是bag了。
再看一點奇怪的:
// 注意 box.getName 沒有括號 (false || box.getName)(); // window (false ? alert : box.getName)(); // window
上面這兩種情況下,輸出的都不再是box對象的name屬性,而是window(之前設置了window.name="window")。表示此時方法內部的this指向的是瀏覽器頂層對象window。
可以這么理解:
box對象指向了一個地址M1, box.getName作為box的一個方法,但本身也是對象,它自己也有一個地址M2,只有通過box.getName() 調用時,是從M1中調用M2,所以this指向的是box。上面兩種情況都是直接拿到M2來調用,此時和M1已經沒有任何關系了,this的指向當前代碼塊所在的對象。
全局環境在全局環境中使用this,在瀏覽器中,指的就是頂層對象window。
console.log(this === window); // true function thisIs() { console.log(this === window); } thisIs(); // true
上面代碼說明,不管this是寫在全局環境下,還是一個函數作用域內,只要是在全局環境下運行,this的指向都是頂層對象window。
Node在Node中,this的指向又分成兩種情況。全局環境中,this指向全局對象global;模塊環境中,this指向module.exports。
ES6箭頭函數ES6中新增的箭頭函數里面所使用的this和之前介紹的情況都不一樣了,在箭頭函數中this不隨其運行環境的改變而改變,而是在聲明箭頭函數時,就已經固定下來了。箭頭函數中this的指向就是聲明箭頭函數是所在的對象。
先看一個常規的例子:
function foo() { setTimeout(function() { console.log("name:", this.name); }, 100); } foo(); // name: window foo.call({ name: "an obj" }); // name: window
定義一個函數foo內部使用定時器調用一個匿名函數,此時函數有多層了,this的指向應該是全局對象window,輸出結果證明了這一點。使用foo.call結果也相同的原因是,call替換的是foo函數內的this指向,而輸出的this是在定時器的回調中的,故結果依然是window。
我們再看一下箭頭函數中這一點的表現:
// ES6箭頭函數 function arrow_foo() { setTimeout(() => { console.log("name:", this.name); }, 100); } arrow_foo(); // name: window arrow_foo.call({ name: "an obj" }); // name: an obj
我們發現結果,居然和上面不一樣了。為什么呢?我們將其轉化成ES5的結果來看一下,上面代碼轉化后的結果是這樣的:
function arrow_foo() { var $__1 = this; setTimeout(function() { console.log("name:", $__1.name); }, 100); } arrow_foo(); arrow_foo.call({name: "an obj"});
看一下轉換后的結果,原因就一目了然了,箭頭函數中this直接固定成了其定義時所在的對象,此處為foo。實際在箭頭函數中的所有this都是一個對象,這個對象就是其定義時所在對象的this,上面轉換后的結果中在foo中首先使用一個變量記錄下this,而在箭頭函數中的this被替換成了之前存儲this的那個變量。
因此直接運行時,this是指全局對象,而使用call時,將foo內的this替換成了指定的對象{name: "an obj"},從而輸出的上面的結果。
使用注意點 避免多層this由于this的指向是不確定的,所以切勿在函數中包含多層的this。
var box = { name: "box", size: { width: 300, height: 300 }, show: function() { console.log("name", this.name); (function() { console.log("size", this.size); })(); } }; box.show(); // name box // size undefined
我們本意是想在show方法內部輸出name,并輸出size,但是結果卻并不是想要的這樣,這是因為在立即執行的函數內部,this的執行不再是box對象而變成了頂層對象window,因此第二行輸出為undefined。
解決方法為,在外層用一個變量記錄下this,在要使用的地方使用那個變量。
將上例進行改寫:
var box = { name: "box", size: { width: 300, height: 300 }, show: function() { console.log("name", this.name); var that = this; (function() { console.log("size", that.size); })(); } }; box.show(); // name box // size Object {width: 300, height: 300}
這樣就能得到我們想要的正確結果了。
還用一種做法是JavaScript提供的嚴格模式use strict,如果函數內部的this直接指向了頂層對象會直接報錯。
var box = { name: "box", size: { width: 300, height: 300 }, show: function() { "use strict" console.log("name", this.name); (function() { console.log("size", this.size); })(); } }; box.show(); // Uncaught TypeError: Cannot read property "size" of undefined(…)避免在回調函數中使用this
通常回調函數中的this都有其特定的,如果在回調函數中使用this,應該需要了解其含義,否則可能出現意料之外的結果。
事件處理函數作為一種特殊的回調函數,其this是指當前的DOM對象,最開始的例子已經說明了這個問題。
回調函數本身是一個函數,其作為另一個函數的參數傳遞進去,然后在那個函數內部執行,這本身已經構成了多層this,此時this的指向是不確定的,需要慎用。
綁定this的方法this的動態性給JavaScript帶來了很大的靈活性,但是前面所描述的內容中也表現出了其不確定性,因此有時希望能夠將this固定下來。
function.prototype.call()使用函數的call方法,可以指定函數內部this的指向,使其在指定的作用域中運行。
var obj = {}; var f = function () { return this; }; f() === this; // true this === window f.call(obj) === obj; // true
上面代碼中,在全局環境運行函數f時,this指向全局環境;call方法可以改變this的指向,指定this指向對象obj,然后在對象obj的作用域中運行函數f。
call方法的第一個參數為一個對象,其表示要為函數所指定的運行上下文環境的對象(當指定為undefined或null是默認傳入window),之后的參數依次作為原函數的參數。
function.prototype.apply()使用函數的apply方法同樣可以指定函數運行的環境,作用和call相同,使用方法也類似,都是第一個參數傳入要指定的上下文對象。不同點在于,apply方法最多接收兩個參數,第二個參數為一個數組(無論原函數需要的參數是何種類型,此數組中的每個元素將依次傳遞給原函數),表示傳遞給原函數的參數,而call可以接收多個參數,從第二個參數開始,之后的所有參數都傳遞給原函數。
由于apply第二個參數接收的是數組,其有很多巧用。由于此文重點是描述this關鍵字,就不再贅述了。
function.prototype.bind()ES5中有bind這樣一個方法,也可以指定函數的運行環境,但是和call、apply有所不同,bind方法可接收一個參數,用于指定函數運行的上下文環境,返回一個函數作為綁定指定上下文環境后的新函數。
這樣bind和call、apply的區別就出來了:前者是根據指定的上下文環境返回一個新函數,而后兩者是使用指定的上下文壞境運行原函數。
其實bind和jQuery.proxy()類似,雖然沒有后者處理多種情況,但作為JavaScript原生方法,更輕量、高效。
用本文最開始的例子來演示此方法的使用,某對象下有某方法,我們要將此對象這個方法作為作為一個事件處理函數,但不希望方法內部的this被改變:
這樣點擊第二個按鈕,將可以正確輸出張三的名字。
bind第一個參數為一個對象,為undefined或null是默認傳入window。
除此之外,bind還可以接收額外參數,用于在生成新函數時,從原函數的第一個參數開始替換一部分參數(和jQuery.proxy()類似)。比如原函數要接收兩個參數,使用bind產生新函數時,除了第一個參數的外,可以再傳入一個參數,此參數將替換原函數的第一個參數,這樣生成的新函數就只用接收一個參數了,詳情見jQuery 工具方法簡析 (target=_blank)中jQuery.proxy( function, context [, additionalArguments ] )。
參考鏈接this 關鍵字 (target=_blank)
深入理解上下文this (target=_blank)
ES6在線轉化 (target=_blank)
js構造函數 (target=_blank)
原文發表在我的博客JavaScript this關鍵字,歡迎訪問!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/82151.html
摘要:當談到語言與其他編程語言相比時,你可能會聽到一些令人困惑東西,其中之一是工廠函數和構造函數。好的,讓我們用構造函數做同樣的實驗。當我們使用工廠函數創建對象時,它的指向,而當從構造函數創建對象時,它指向它的構造函數原型對象。 showImg(https://segmentfault.com/img/bVbr58T?w=1600&h=900); 當談到JavaScript語言與其他編程語言...
摘要:它代表函數運行時,自動生成的一個內部對象,只能在函數內部使用類似的還有。總結關鍵字就是,誰調用我,我就指向誰。注意由于已經被定義為函數內的一個變量。因此通過關鍵字定義或者將聲明為一個形式參數,都將導致原生的不會被創建。 題目 封裝函數 f,使 f 的 this 指向指定的對象 。 輸入例子 bindThis(function(a, b) { return this.test +...
摘要:中函數的調用有以下幾種方式作為對象方法調用,作為函數調用,作為構造函數調用,和使用或調用。作為構造函數調用中的構造函數也很特殊,構造函數,其實就是通過這個函數生成一個新對象,這時候的就會指向這個新對象如果不使用調用,則和普通函數一樣。 this 是 JavaScript 比較特殊的關鍵字,本文將深入淺出的分析其在不同情況下的含義,可以這樣說,正確掌握了 JavaScript 中的 th...
摘要:原文許多人被中的關鍵字給困擾住了,我想混亂的根源來自人們理所當然地認為中的應該像中的或中的一樣工作。盡管有點難理解,但它的原理并不神秘。在瀏覽器中,全局對象是對象。運算符創建一個新對象并且設置函數中的指向調用函數的新對象。 原文:Understanding the this keyword in JavaScript 許多人被JavaScript中的this關鍵字給困擾住了,我想混亂的...
摘要:的關鍵字總是讓人捉摸不透,關鍵字代表函數運行時,自動生成的一個內部對象,只能在函數內部使用,因為函數的調用場景不同,的指向也不同。其實只要理解語言的特性就很好理解。個人對中的關鍵字的理解如上,如有不正,望指正,謝謝。 javascript的this關鍵字總是讓人捉摸不透,this關鍵字代表函數運行時,自動生成的一個內部對象,只能在函數內部使用,因為函數的調用場景不同,this的指向也不...
摘要:關鍵字計算為當前執行上下文的屬性的值。毫無疑問它將指向了這個前置的對象。構造函數也是同理。嚴格模式無論調用位置,只取顯式給定的上下文綁定的,通過方法傳入的第一參數,否則是。其實并不屬于特殊規則,是由于各種事件監聽定義方式本身造成的。 this 是 JavaScript 中非常重要且使用最廣的一個關鍵字,它的值指向了一個對象的引用。這個引用的結果非常容易引起開發者的誤判,所以必須對這個關...
閱讀 1438·2023-04-25 16:31
閱讀 2040·2021-11-24 10:33
閱讀 2746·2021-09-23 11:33
閱讀 2528·2021-09-23 11:31
閱讀 2900·2021-09-08 09:45
閱讀 2336·2021-09-06 15:02
閱讀 2647·2019-08-30 14:21
閱讀 2314·2019-08-30 12:56