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