摘要:事件處理函數(shù)中的把函數(shù)綁定到事件時,可以當(dāng)作在上增加一個函數(shù)方法,當(dāng)觸發(fā)這個事件時調(diào)用上對應(yīng)的事件方法??偨Y(jié)在需要判斷的指向時,我們可以安裝這種思路來理解判斷在全局中函數(shù)中,若在全局中則指向全局,若在函數(shù)中則只關(guān)注這個函數(shù)并繼續(xù)判斷。
在網(wǎng)上很多文章都對 Javascript 中的 this 做了詳細(xì)的介紹,但大多是介紹各個綁定方式或調(diào)用方式下 this 的指向,于是我想有一個統(tǒng)一的思路來更好理解 this 指向,使大家更好判斷,以下有部分內(nèi)容不是原理,而是一種解題思路。
從call方法開始call 方法允許切換函數(shù)執(zhí)行的上下文環(huán)境(context),即 this 綁定的對象。
大多數(shù)介紹 this 的文章中都會把 call 方法放到最后介紹,但此文我們要把 call 方法放在第一位介紹,并從 call 方法切入來研究 this ,因為 call 函數(shù)是顯式綁定 this 的指向,我們來看看它如何模擬實現(xiàn)(不考慮傳入 null 、 undefined 和原始值):
Function.prototype.call = function(thisArg) { var context = thisArg; var arr = []; var result; context.fn = this; for (let i = 1, len = arguments.length; i < len; i++) { arr.push("arguments[" + i + "]"); } result = eval("context.fn(" + arr + ")"); delete context.fn; return result; }
從以上代碼我們可以看到,把調(diào)用 call 方法的函數(shù)作為第一個參數(shù)對象的方法,此時相當(dāng)于把第一個參數(shù)對象作為函數(shù)執(zhí)行的上下文環(huán)境,而 this 是指向函數(shù)執(zhí)行的上下文環(huán)境的,因此 this 就指向了第一個參數(shù)對象,實現(xiàn)了 call 方法切換函數(shù)執(zhí)行上下文環(huán)境的功能。
對象方法中的this在模擬 call 方法的時候,我們使用了對象方法來改變 this 的指向。調(diào)用對象中的方法時,會把對象作為方法的上下文環(huán)境來調(diào)用。
既然 this 是指向執(zhí)行函數(shù)的上下文環(huán)境的,那我們先來研究一下調(diào)用函數(shù)時的執(zhí)行上下文情況。
下面我門來看看調(diào)用對象方法時執(zhí)行上下文是如何的:
var foo = { x : 1, getX: function(){ console.log(this.x); } } foo.getX();
從上圖中,我們可以看出getX方法的調(diào)用者的上下文是foo,因此getX方法中的 this 指向調(diào)用者上下文foo,轉(zhuǎn)換成 call 方法為foo.getX.call(foo)。
下面我們把其他函數(shù)的調(diào)用方式都按調(diào)用對象方法的思路來轉(zhuǎn)換。
構(gòu)造函數(shù)中的thisfunction Foo(){ this.x = 1; this.getX = function(){ console.log(this.x); } } var foo = new Foo(); foo.getX();
執(zhí)行 new 如果不考慮原型鏈,只考慮上下文的切換,就相當(dāng)于先創(chuàng)建一個空的對象,然后把這個空的對象作為構(gòu)造函數(shù)的上下文,再去執(zhí)行構(gòu)造函數(shù),最后返回這個對象。
var newMethod = function(func){ var context = {}; func.call(context); return context; } function Foo(){ this.x = 1; this.getX = function(){ console.log(this.x); } } var foo = newMethod(Foo); foo.getX();DOM事件處理函數(shù)中的this
DOMElement.addEventListener("click", function(){ console.log(this); });
把函數(shù)綁定到DOM事件時,可以當(dāng)作在DOM上增加一個函數(shù)方法,當(dāng)觸發(fā)這個事件時調(diào)用DOM上對應(yīng)的事件方法。
DOMElement.clickHandle = function(){ console.log(this); } DOMElement.clickHandle();普通函數(shù)中的this
var x = 1; function getX(){ console.log(this.x); } getX();
這種情況下,我們創(chuàng)建一個虛擬上下文對象,然后普通函數(shù)作為這個虛擬上下文對象的方法調(diào)用,此時普通函數(shù)中的this就指向了這個虛擬上下文。
那這個虛擬上下文是什么呢?在非嚴(yán)格模式下是全局上下文,瀏覽器里是 window ,NodeJs里是 Global ;在嚴(yán)格模式下是 undefined 。
var x = 1; function getX(){ console.log(this.x); } [viturl context].getX = getX; [viturl context].getX();閉包中的this
var x = 1; var foo = { x: 2, y: 3, getXY: function(){ (function(){ console.log("x:" + this.x); console.log("y:" + this.y); })(); } } foo.getXY();
這段代碼的上下文如下圖:
這里需要注意的是,我們再研究函數(shù)中的 this 指向時,只需要關(guān)注 this 所在的函數(shù)是如何調(diào)用的, this 所在函數(shù)外的函數(shù)調(diào)用都是浮云,是不需要關(guān)注的。因此在所有的圖示中,我們只需要關(guān)注紅色框中的內(nèi)容。
因此這段代碼我們關(guān)注的部分只有:
(function(){ console.log(this.x); })();
與普通函數(shù)調(diào)用一樣,創(chuàng)建一個虛擬上下文對象,然后普通函數(shù)作為這個虛擬上下文對象的方法立即調(diào)用,匿名函數(shù)中的 this 也就指向了這個虛擬上下文。
var x = 1; var foo = { x: 2, getX: function(){ console.log(this.x); } } setTimeout(foo.getX, 1000);
函數(shù)參數(shù)是值傳遞的,因此上面代碼等同于以下代碼:
var getX = function(){ console.log(this.x); }; setTimeout(getX, 1000);
然后我們又回到了普通函數(shù)調(diào)用的問題。
全局中的this全局中的 this 指向全局的上下文
var x = 1; console.log(this.x);復(fù)雜情況下的this
var x = 1; var a = { x: 2, b: function(){ return function(){ return function foo(){ console.log(this.x); } } } }; (function(){ var x = 3; a.b()()(); })();
看到上面的情況是有很多個函數(shù),但我們只需要關(guān)注 this 所在函數(shù)的調(diào)用方式,首先我們來簡化一下如下:
var x = 1; (function(){ var x = 3; var foo = function(){ console.log(this.x); } foo(); });
this 所在的函數(shù) foo 是個普通函數(shù),我們創(chuàng)建一個虛擬上下文對象,然后普通函數(shù)作為這個虛擬上下文對象的方法立即調(diào)用。因此這個 this指向了這個虛擬上下文。在非嚴(yán)格模式下是全局上下文,瀏覽器里是 window ,NodeJs里是 Global ;在嚴(yán)格模式下是 undefined 。
總結(jié)在需要判斷 this 的指向時,我們可以安裝這種思路來理解:
判斷 this 在全局中OR函數(shù)中,若在全局中則 this 指向全局,若在函數(shù)中則只關(guān)注這個函數(shù)并繼續(xù)判斷。
判斷 this 所在函數(shù)是否作為對象方法調(diào)用,若是則 this 指向這個對象,否則繼續(xù)操作。
創(chuàng)建一個虛擬上下文,并把this所在函數(shù)作為這個虛擬上下文的方法,此時 this 指向這個虛擬上下文。
在非嚴(yán)格模式下虛擬上下文是全局上下文,瀏覽器里是 window ,Node.js里是 Global ;在嚴(yán)格模式下是 undefined 。
圖示如下:
歡迎關(guān)注:Leechikit
原文鏈接:segmentfault.com到此本文結(jié)束,歡迎提問和指正。
寫原創(chuàng)文章不易,若本文對你有幫助,請點贊、推薦和關(guān)注作者支持。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/84283.html
摘要:但是有一個總的原則那就是總會指向,調(diào)用函數(shù)的那個對象。作為對象方法的調(diào)用函數(shù)作為某個對象的方法調(diào)用,這時就指這個上級對象。 showImg(https://segmentfault.com/img/bVbnvF7?w=750&h=422); 這是前端面試題系列的第 4 篇,你可能錯過了前面的篇章,可以在這里找到: 偽類與偽元素的區(qū)別及實戰(zhàn) 如何實現(xiàn)一個圣杯布局? 今日頭條 面試題和思...
摘要:前端日報精選三思而后行想提高團隊技術(shù),來試試這個套路如何開發(fā)一個插件學(xué)習(xí)筆記塊級作用域綁定譯文詳解帶來的個重大變化中文周二放送畫圖知乎專欄第期新特性譯配置譯高性能視差滾動行代碼構(gòu)建區(qū)塊鏈知乎專欄渲染器修仙之路之拷貝對象已 2017-07-25 前端日報 精選 SSR 三思而后行想提高團隊技術(shù),來試試這個套路!如何開發(fā)一個 Atom 插件ES6學(xué)習(xí)筆記:塊級作用域綁定【譯文】詳解VUE2...
摘要:我們已經(jīng)回答了的構(gòu)造函數(shù)和原型都是誰的問題,現(xiàn)在牽扯出來一個,我們繼續(xù)檢查的構(gòu)造函數(shù)是全局對象上屬性叫的對象的原型是個匿名函數(shù),按照關(guān)于構(gòu)造函數(shù)的約定,它應(yīng)該是構(gòu)造函數(shù)的屬性我們給這個對象起個名字,叫。 我不確定JavaScript語言是否應(yīng)該被稱為Object-Oriented,因為Object Oriented是一組語言特性、編程模式、和設(shè)計與工程方法的籠統(tǒng)稱謂,沒有一個詳盡和大家...
摘要:我們就需要我們自己去定義事件其實就是我們寫的函數(shù),尤其是組件開發(fā)過程中,用的尤為多??赡苡写_定按鈕取消按鈕等操作。但是自定義事件的基本原理就是如上描繪的那樣 我們都知道,鼠標(biāo)點擊click,觸屏的touch等事件,可以觸發(fā)相應(yīng)的事件處理程序,也可以為這些事件添加事件處理程序,實際開發(fā)過程中可供我們使用的事件很少,click、doubleclick,mouseover、mousemove...
摘要:之前做過的組件配置系統(tǒng)核心就是它。但是如果你想改動到這個元素的樣式,但是又不想改動公用模板。 1.placeholder與computed巧用 表單開發(fā)肯定是日常開發(fā)中必不可少的環(huán)節(jié),可是設(shè)計圖經(jīng)常會有表單默認(rèn)值的設(shè)計,比如:showImg(https://segmentfault.com/img/remote/1460000012955169?w=559&h=392); 需求方的需求...
閱讀 3194·2021-11-12 10:36
閱讀 5082·2021-09-22 10:57
閱讀 1818·2021-09-22 10:53
閱讀 2830·2019-08-30 15:55
閱讀 3573·2019-08-29 17:00
閱讀 3435·2019-08-29 16:36
閱讀 2537·2019-08-29 13:46
閱讀 1429·2019-08-26 11:45