JavaScript this關(guān)鍵字的深入詳解
一、前言
this關(guān)鍵字是JavaScript中最復(fù)雜的機(jī)制之一。它是一個(gè)很特別的關(guān)鍵字,被自動(dòng)定義在所有函數(shù)的作用域中。對(duì)于那些沒(méi)有投入時(shí)間學(xué)習(xí)this機(jī)制的JavaScript開(kāi)發(fā)者來(lái)說(shuō),this的綁定一直是一件非常令人困惑的事。
二、了解this
學(xué)習(xí)this的第一步是明白this既不指向函數(shù)自身也不指向函數(shù)的詞法作用域,你也許被這樣的解釋誤導(dǎo)過(guò),但其實(shí)它們都是錯(cuò)誤的。隨著函數(shù)使用場(chǎng)合的不同,this的值會(huì)發(fā)生變化。但總有一條原則就是 JS中的this代表的是當(dāng)前行為執(zhí)行的主體 ,在JS中主要研究的都是函數(shù)中的this,但并不是說(shuō)只有在函數(shù)里才有this, this實(shí)際上是在函數(shù)被調(diào)用時(shí)發(fā)生的綁定,它指向什么完全取決于函數(shù)在哪里被調(diào)用 。如何的區(qū)分this呢?
三、this到底是誰(shuí)
這要分情況討論,常見(jiàn)有五種情況:
1、函數(shù)執(zhí)行時(shí)首先看函數(shù)名前面是否有".",有的話,"."前面是誰(shuí),this就是誰(shuí);沒(méi)有的話this就是window
function fn(){ console.log(this); } var obj={fn:fn}; fn();//this->window obj.fn();//this->obj function sum(){ fn();//this->window } sum(); var oo={ sum:function(){ console.log(this);//this->oo fn();//this->window } }; oo.sum();
2、自執(zhí)行函數(shù)中的this永遠(yuǎn)是window
(function(){ //this->window })(); ~function(){ //this->window }();
3、給元素的某一個(gè)事件綁定方法,當(dāng)事件觸發(fā)的時(shí)候,執(zhí)行對(duì)應(yīng)的方法,方法中的this是當(dāng)前的元素,除了IE6~8下使用attachEvent(IE一個(gè)著名的bug)
DOM零級(jí)事件綁定
oDiv.onclick=function(){ //this->oDiv };
DOM二級(jí)事件綁定
oDiv.addEventListener("click",function(){ //this->oDiv },false);
在IE6~8下使用attachEvent,默認(rèn)的this就是指的window對(duì)象
oDiv.attachEvent("click",function(){ //this->window });
我們大多數(shù)時(shí)候,遇到事件綁定,如下面例子這種,對(duì)于IE6~8下使用attachEvent不必太較真
function fn(){ console.log(this); } document.getElementById("div1").onclick=fn;//fn中的this就是#divl document.getElementById("div1").onclick=function(){ console.log(this);//this->#div1 fn();//this->window };
4、在構(gòu)造函數(shù)模式中,類(lèi)中(函數(shù)體中)出現(xiàn)的this.xxx=xxx中的this是當(dāng)前類(lèi)的一個(gè)實(shí)例
function CreateJsPerson(name,age){ //瀏覽器默認(rèn)創(chuàng)建的對(duì)象就是我們的實(shí)例p1->this this.name=name;//->p1.name=name this.age=age; this.writeJs=function(){ console.log("my name is"+this.name +",i can write Js"); }; //瀏覽器再把創(chuàng)建的實(shí)例默認(rèn)的進(jìn)行返回 } var p1=new CreateJsPerson("尹華芝",48);
必須要注意一點(diǎn): 類(lèi)中某一個(gè)屬性值(方法),方法中的this需要看方法執(zhí)行的時(shí)候,前面是否有".",才能知道this是誰(shuí) 。大家不妨看下接下來(lái)的這個(gè)例子,就可明白是啥意思。
function Fn(){ this.x=100;//this->f1 this.getX=function(){ console.log(this.x);//this->需要看getX執(zhí)行的時(shí)候才知道 } } var f1=new Fn; f1.getX();//->方法中的this是f1,所以f1.x=100 var ss=f1.getX; ss();//->方法中的this是window ->undefined
5.call、apply和bind
我們先來(lái)看一個(gè)問(wèn)題,想在下面的例子中this綁定obj,怎么實(shí)現(xiàn)?
var obj={name:"浪里行舟"}; function fn(){ console.log(this);//this=>window } fn(); obj.fn();//->Uncaught TypeError:obj.fn is not a function
如果直接綁定obj.fn(),程序就會(huì)報(bào)錯(cuò)。這里我們應(yīng)該用fn.call(obj)就可以實(shí)現(xiàn)this綁定obj,接下來(lái)我們?cè)敿?xì)介紹下call方法:
call方法的作用:
①首先我們讓原型上的call方法執(zhí)行,在執(zhí)行call方法的時(shí)候,我們讓fn方法中的this變?yōu)榈谝粋€(gè)參數(shù)值obj;然后再把fn這個(gè)函數(shù)執(zhí)行。
②call還可以傳值,在嚴(yán)格模式下和非嚴(yán)格模式下,得到值不一樣。
//在非嚴(yán)格模式下 var obj={name:"浪里行舟 "}; function fn(num1,num2){ console.log(num1+num2); console.log(this); } fn.call(100,200);//this->100 num1=200 num2=undefined fn.call(obj,100,200);//this->obj num1=100 num2=200 fn.call();//this->window fn.call(null);//this->window fn.call(undefined);//this->window
//嚴(yán)格模式下 fn.call();//在嚴(yán)格模式下this->undefined fn.call(null);// 在嚴(yán)格模式 下this->null fn.call(undefined);//在嚴(yán)格模式下this->undefined
**apply和call方法的作用是一模一樣的,都是用來(lái)改變方法的this關(guān)鍵字并且把方法
執(zhí)行,而且在嚴(yán)格模式下和非嚴(yán)格模式下對(duì)于第一個(gè)參數(shù)是null/undefined這種情況的規(guī)
律也是一樣的。**
兩者唯一的區(qū)別:call在給fn傳遞參數(shù)的時(shí)候,是一個(gè)個(gè)的傳遞值的,而apply不是一個(gè)個(gè)傳,而是把要給fn傳遞的參數(shù)值統(tǒng)一的放在一個(gè)數(shù)組中進(jìn)行操作。但是也相當(dāng)子一個(gè)個(gè)的給fn的形參賦值。 總結(jié)一句話:call第二個(gè)參數(shù)開(kāi)始接受一個(gè)參數(shù)列表,apply第二個(gè)參數(shù)開(kāi)始接受一個(gè)參數(shù)數(shù)組
fn.call(obj,100,200); fn.apply(obj,[100,200]);
bind:這個(gè)方法在IE6~8下不兼容,和call/apply類(lèi)似都是用來(lái)改變this關(guān)鍵字的 ,但是和這兩者有明顯區(qū)別:
fn.call(obj,1,2);//->改變this和執(zhí)行fn函數(shù)是一起都完成了
fn.bind(obj,1,2);//->只是改變了fn中的this為obj,并且給fn傳遞了兩個(gè)參數(shù)值1、2, 但是此時(shí)并沒(méi)有把fn這個(gè)函數(shù)執(zhí)行 var tempFn=fn.bind(obj,1,2); tempFn(); //這樣才把fn這個(gè)函數(shù)執(zhí)行
bind體現(xiàn)了預(yù)處理思想:事先把fn的this改變?yōu)槲覀兿胍慕Y(jié)果,并且把對(duì)應(yīng)的參數(shù)值也準(zhǔn)備好,以后要用到了,直接的執(zhí)行即可。
call和apply直接執(zhí)行函數(shù),而bind需要再一次調(diào)用。
var a ={ name : "Cherry", fn : function (a,b) { console.log( a + b) } } var b = a.fn; b.bind(a,1,2)
上述代碼沒(méi)有執(zhí)行,bind返回改變了上下文的一個(gè)函數(shù),我們必須要手動(dòng)去調(diào)用:
b.bind(a,1,2)() //3
必須要聲明一點(diǎn):遇到第五種情況(call apply和bind),前面四種全部讓步。
四、箭頭函數(shù)this指向
箭頭函數(shù)正如名稱(chēng)所示那樣使用一個(gè)“箭頭”(=>)來(lái)定義函數(shù)的新語(yǔ)法,但它優(yōu)于傳統(tǒng)的函數(shù),主要體現(xiàn)兩點(diǎn): 更簡(jiǎn)短的函數(shù)并且不綁定this 。
var obj = { birth: 1990, getAge: function () { var b = this.birth; // 1990 var fn = function () { return new Date().getFullYear() - this.birth; // this指向window或undefined }; return fn(); } };
現(xiàn)在,箭頭函數(shù)完全修復(fù)了this的指向, 箭頭函數(shù)沒(méi)有自己的this,箭頭函數(shù)的this不是調(diào)用的時(shí)候決定的,而是在定義的時(shí)候處在的對(duì)象就是它的this 。
換句話說(shuō), 箭頭函數(shù)的this看外層的是否有函數(shù),如果有,外層函數(shù)的this就是內(nèi)部箭頭函數(shù)的this,如果沒(méi)有,則this是window 。
<button id="btn1">測(cè)試箭頭函數(shù)this_1</button> <button id="btn2">測(cè)試箭頭函數(shù)this_2</button> <script type="text/javascript"> let btn1 = document.getElementById('btn1'); let obj = { name: 'kobe', age: 39, getName: function () { btn1.onclick = () => { console.log(this);//obj }; } }; obj.getName(); </script>
上例中,由于箭頭函數(shù)不會(huì)創(chuàng)建自己的this,它只會(huì)從自己的作用域鏈的上一層繼承this。其實(shí)可以簡(jiǎn)化為如下代碼:
let btn1 = document.getElementById('btn1'); let obj = { name: 'kobe', age: 39, getName: function () { console.log(this) } }; obj.getName();
那假如上一層并不存在函數(shù),this指向又是誰(shuí)?
<button id="btn1">測(cè)試箭頭函數(shù)this_1</button> <button id="btn2">測(cè)試箭頭函數(shù)this_2</button> <script type="text/javascript"> let btn2 = document.getElementById('btn2'); let obj = { name: 'kobe', age: 39, getName: () => { btn2.onclick = () => { console.log(this);//window }; } }; obj.getName(); </script>
上例中,雖然存在兩個(gè)箭頭函數(shù),其實(shí)this取決于最外層的箭頭函數(shù),由于obj是個(gè)對(duì)象而非函數(shù),所以this指向?yàn)閃indow對(duì)象
由于this在箭頭函數(shù)中已經(jīng)按照詞法作用域綁定了,所以, 用call()或者apply()調(diào)用箭頭函數(shù)時(shí),無(wú)法對(duì)this進(jìn)行綁定,即傳入的第一個(gè)參數(shù)被忽略 :
var obj = { birth: 1990, getAge: function (year) { var b = this.birth; // 1990 var fn = (y) => y - this.birth; // this.birth仍是1990 return fn.call({birth:2000}, year); } }; obj.getAge(2018); // 28
擴(kuò)展閱讀
總結(jié)
到此這篇關(guān)于JavaScript this關(guān)鍵字深入詳解的文章就介紹到這了,更多相關(guān)JavaScript this關(guān)鍵字內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
微信小程序一周時(shí)間表功能實(shí)現(xiàn)
這篇文章主要介紹了微信小程序一周時(shí)間表功能實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10JavaScript 實(shí)現(xiàn)鼠標(biāo)拖動(dòng)元素實(shí)例代碼
這篇文章主要介紹了JavaScript 實(shí)現(xiàn)鼠標(biāo)拖動(dòng)元素實(shí)例代碼,需要的朋友可以參考下2014-02-02將form表單中的元素轉(zhuǎn)換成對(duì)象的方法適用表單提交
這篇文章主要介紹了如何將form表單中的元素轉(zhuǎn)換成對(duì)象,需要的朋友可以參考下2014-05-05js實(shí)現(xiàn)簡(jiǎn)單登錄功能的實(shí)例代碼
js驗(yàn)證用戶身份,登錄成功之后等待一定秒數(shù),跳轉(zhuǎn)到操作頁(yè)面。使用window函數(shù)。代碼如下2013-11-11在js中判斷checkboxlist(.net控件客戶端id)是否有選中
添加或修改內(nèi)容時(shí),需要對(duì)關(guān)鍵數(shù)據(jù)進(jìn)行判空處理,checkboxlist是否有選擇項(xiàng)如何使用js判斷實(shí)現(xiàn),接下來(lái)為大家詳細(xì)介紹下實(shí)現(xiàn)方法,感興趣的朋友可以參考下哈2013-04-04JS實(shí)現(xiàn)讀取xml內(nèi)容并輸出到div中的方法示例
這篇文章主要介紹了JS實(shí)現(xiàn)讀取xml內(nèi)容并輸出到div中的方法,涉及javascript針對(duì)xml格式數(shù)據(jù)的讀取、遍歷、輸出等相關(guān)操作技巧,需要的朋友可以參考下2018-04-04JavaScript極簡(jiǎn)入門(mén)教程(一):基礎(chǔ)篇
這篇文章主要介紹了JavaScript極簡(jiǎn)入門(mén)教程(一):基礎(chǔ)篇,本文講解了JavaScript的基礎(chǔ)語(yǔ)法、操作符、運(yùn)算符等內(nèi)容,需要的朋友可以參考下2014-10-10JAVASCRIPT車(chē)架號(hào)識(shí)別/驗(yàn)證函數(shù)代碼 汽車(chē)車(chē)架號(hào)驗(yàn)證程序
偶然中在CSDN里找到C#版的驗(yàn)證程序,因此改編了一版JS版本,相信會(huì)對(duì)大家有用2012-01-01