摘要:全局上下文在全局中,一律指向全局對(duì)象。特殊的以下情況中的需要進(jìn)行特殊記憶。構(gòu)造函數(shù)當(dāng)一個(gè)函數(shù)作為構(gòu)造函數(shù)使用時(shí),構(gòu)造函數(shù)的指向由該構(gòu)造函數(shù)出來(lái)的對(duì)象。舉例使用綁定時(shí),監(jiān)聽(tīng)函數(shù)中的指向觸發(fā)事件的,表示被綁定了監(jiān)聽(tīng)函數(shù)的元素。執(zhí)行與執(zhí)行同理。
我的博客地址 → this | The story of Captain,轉(zhuǎn)載請(qǐng)注明出處。
問(wèn):this 是什么?
答:this 是 call 方法的第一個(gè)參數(shù),call 的第一個(gè)參數(shù)就是 this。
完。
就這么簡(jiǎn)單么?是的。
為什么這樣說(shuō)?因?yàn)樗械暮瘮?shù)/方法調(diào)用的時(shí)候都可以 轉(zhuǎn)換 為 call 形式,call 的第一個(gè)參數(shù)顯式的指明了函數(shù)該次執(zhí)行時(shí)候的上下文。
今天我們深入探討一下如何確定 this。
如何確定 this ?this 由函數(shù)的上下文確定。
如何確定“上下文” ?上下文分為 全局上下文(Global Context) 以及 函數(shù)上下文(Function Context)。
全局上下文在全局中,this 一律指向 全局對(duì)象 window。例如:
console.log(this === window); //; true函數(shù)上下文
在函數(shù)中,上下文由函數(shù)被調(diào)用的方式?jīng)Q定。
以 “函數(shù)名( )” 形式調(diào)用的函數(shù)即為簡(jiǎn)單調(diào)用,簡(jiǎn)單調(diào)用時(shí)上下文為全局上下文,因此 this === window 。
舉例一:
function foo () { console.log(this === window); } foo(); // true
舉例二:
function fn1 () { function fn2 () { console.log(this === window); } fn2(); } fn1(); // true,因?yàn)?fn2 為簡(jiǎn)單調(diào)用
舉例三:
let obj = { fn1: function () { console.log(this === window); } }; let fn2 = obj.fn1; fn2(); // true
第三個(gè)例子中,為什么 fn2() 執(zhí)行結(jié)果為 true ?因?yàn)閳?zhí)行了 let fn2 = obj.fn1 之后 fn2 為:
fn2 = function () { console.log(this); }
再執(zhí)行 fn2() 時(shí),為簡(jiǎn)單調(diào)用,因此 this === window 。
當(dāng)函數(shù)作為一個(gè)對(duì)象的方法被調(diào)用時(shí),this 指向該對(duì)象。
舉例一:
let obj = { fn1: function () { console.log(this === obj); } }; obj.fn1(); // true
以 obj.fn1() 形式調(diào)用 fn1 時(shí),是以方法形式調(diào)用的,this 指向該函數(shù)所屬的對(duì)象,即 obj。
舉例二:
let obj = { fn1: { fn2:function () { console.log(this === obj.fn1); } } }; obj.fn1.fn2(); // true
以 obj.fn1.fn2() 形式調(diào)用 fn2 時(shí),是以方法形式調(diào)用的,this 指向該函數(shù)所屬的對(duì)象,即 obj.fn1,很多人常誤以為此處的 this 指向 obj,這是錯(cuò)誤的。
舉例三:
let obj = { fn1: function () { return function () { console.log(this === window); } } }; let fn2 = obj.fn1(); fn2(); // true
為什么 fn2() 的執(zhí)行結(jié)果為 true ?因?yàn)閳?zhí)行了 let fn2 = obj.fn1() 之后 fn2 為:
fn2 = function () { console.log(this === window); }
再執(zhí)行 fn2() 時(shí),為簡(jiǎn)單調(diào)用,因此 this === window 。如果想要將 fn2 中的 this 指向 obj,可將指向 obj 的 this 保存在中間變量,改動(dòng)如下所示:
let obj = { fn1: function () { let that = this; return function () { console.log(that === obj); } } }; let fn2 = obj.fn1(); fn2(); // true
利用 let that = this 將 fn1 中的 this 保存在 that 變量中,然后 fn2() 的結(jié)果即為 true,當(dāng)然這其中涉及到了 閉包(closure) 的知識(shí)。
特殊的 this以下情況中的 this 需要進(jìn)行特殊記憶。
箭頭函數(shù)箭頭函數(shù)(arrow function,=>),箭頭函數(shù)為 ES6 中引入的新的函數(shù)表示法,不同之處在于,箭頭函數(shù)中沒(méi)有 this,箭頭函數(shù)中的 this 為其執(zhí)行上下文中的 this,如何理解?舉例說(shuō)明。
舉例一:
() => console.log(this === window); // true
其執(zhí)行上下文為全局上下文,this 指向 window。
舉例二:
function foo () { return () => console.log(this === window); }; foo()(); // true
和方法調(diào)用中的舉例三類(lèi)似。
舉例三:
let obj = { fn1: () => console.log(this === window); }; obj.fn1(); // true
為什么是 true ?方法調(diào)用中的舉例一中的 this 不是 obj 嗎?沒(méi)錯(cuò),箭頭函數(shù) fn1 中是沒(méi)有自己的 this 的,因此 this 不指向 obj ,繼續(xù)向上找 obj 的上一級(jí),直到找到有 this 的上下文為止,obj 處在全局上下文中, 全局上下文中有 this,因此箭頭函數(shù)中的 this 為全局上下文中的 this,即 指向 window。
舉例四:
let obj = { fn1: function () { return () => console.log(this === obj); } }; let fn2 = obj.fn1(); fn2(); // true
此處又和方法調(diào)用的舉例三不同,因?yàn)榧^函數(shù)中是沒(méi)有自己的 this 的,箭頭函數(shù)中的 this 為其上一級(jí)的 this ,因此,箭頭函數(shù)中的 this 為其上一級(jí),即 fn1 中的 this,fn1 中的 this 指向 obj,所以箭頭函數(shù)中的 this 指向 obj。根據(jù)箭頭函數(shù)的特性:箭頭函數(shù)中的 this 保留了其上一級(jí)的 this 指向,那么方法調(diào)用舉例三的改動(dòng)可以?xún)?yōu)化為本例所示,用一個(gè)箭頭函數(shù)即可解決,省去了中間變量。
構(gòu)造函數(shù)當(dāng)一個(gè)函數(shù)作為構(gòu)造函數(shù)使用時(shí),構(gòu)造函數(shù)的 this 指向由該構(gòu)造函數(shù) new 出來(lái)的對(duì)象。舉例說(shuō)明:
function CreateNewPerson (name,gender,age) { this.name = name; this.gender = gender; this.age = age; } let me = new CreateNewPerson("daijt","male",18); console.log(me.name); // "daijt" console.log(me.gender); // "male" console.log(me.age); // 18
執(zhí)行 let me = new CreateNewPerson("daijt","male",18) 時(shí),構(gòu)造函數(shù)中的 this 直接指向由其 new 出來(lái)對(duì)象對(duì)象 me ,因此執(zhí)行完該句后 me 的結(jié)構(gòu)如下:
me = { name: "daijt", gender: "male", age: 18 }原型鏈
舉例一:
let name = new String("daijt"); name.toUpperCase(); // DAIJT
根據(jù)上文構(gòu)造函數(shù)中的 this,執(zhí)行 let name = new String("daijt") 時(shí),String 構(gòu)造函數(shù)中的 this 指向了 name,而 name 有 __proto__ 屬性,該屬性指向所有 string 類(lèi)的共有屬性或者方法,而這些共有的屬性和方法都保存在 String.prototype 中,即:
name.__proto__ === String.prototype; // true
因此 name 是有 toUpperCase 方法的(原型鏈繼承而來(lái)),調(diào)用 toUpperCase 時(shí),toUpperCase 中的 this 指向 name,因此 name.toUpperCase() 的結(jié)果為 DAIJT 。
舉例二:
let name = "daijt"; name.toUpperCase.(); // DAIJT
為何沒(méi)有通過(guò) new 出來(lái)的對(duì)象也具有 toUpperCase 方法呢?因?yàn)樵趫?zhí)行 let name = "daijt" 的過(guò)程中,JS 有一個(gè)臨時(shí)轉(zhuǎn)化的過(guò)程,例如:
let name = (function (string) { return new String(string); })("daijt");
因此,name 也繼承了 string 類(lèi)共有的屬性和方法,這也算是 JS 的一個(gè)語(yǔ)法糖吧。 當(dāng)然,這涉及到了其他的知識(shí)。
DOM EventHandle舉例:
let buttons = document.querySelector("button"); buttons.addEventListener("click", function (event) { console.log(this === event.currentTarget); // true });
使用 addEventListener 綁定 DOM 時(shí),監(jiān)聽(tīng)函數(shù)中的 this 指向觸發(fā)事件的 currentTarget,currentTarget 表示被綁定了監(jiān)聽(tīng)函數(shù)的 DOM 元素。
注意:如果是通過(guò)冒泡觸發(fā)監(jiān)聽(tīng)函數(shù)的話,event.target 不一定等于 event.currentTarget 。jQuery EventHandle
HTML:
JavaSctipt:
$("#father-ul").on("click", ".father-li", function (event) { console.log(event.target); console.log(event.currentTarget); console.log(this === currentTarget); });
當(dāng)點(diǎn)擊
當(dāng)點(diǎn)擊
因此可以得出結(jié)論:jQuery EventHandle 中的 this 指的是被代理事件監(jiān)聽(tīng)的 DOM 元素,也就是匹配所有選擇器的 DOM 元素,即 .father-li ,具體解釋可參照 jQuery 文檔 。
### 如何改變 this
以上所述的 this 都為確定的 this,那么如何自己設(shè)置 this,改變 this 的指向呢?或者說(shuō)如何動(dòng)態(tài)改變上下文呢?ES5 為我們提供了三個(gè)全局方法:call()、apply()、bind()。三個(gè)方法都可以動(dòng)態(tài)的改變上下文,即 this 的指向,三者的區(qū)別可以參照 MDN,以 call() 為例進(jìn)行說(shuō)明。
var name = "全局上下文"; let me = { name: "daijt", gender: "male". age: 23, }; let myGirlFriend = { name: "xiaofang", gender: "female", age: 18 }; function printName() { console.log(this.name); } printName(); // window printName.call(me); // daijt printName.call(myGirlFriend); // xiaofang
執(zhí)行 printName() 時(shí):
簡(jiǎn)單調(diào)用,因此其內(nèi)部的 this 指向 全局上下文,因此 this === window ,而使用 var 關(guān)鍵字在全局聲明的變量會(huì)作為 window 對(duì)象的屬性,因此 this.name === window.name === 全局上下文 。
執(zhí)行 printName.call(me) 時(shí):
因?yàn)?call() 的第一個(gè)參數(shù)為 thisArg ,因此使用 call() 顯式的指明了 printName 函數(shù)本次執(zhí)行的上下文,即 me,因 this 指向上下文,所以 this === me ,this.name === me.name === daijt 。
執(zhí)行 printName.call(myGirlFriend) 與執(zhí)行 printName.call(me) 同理。
技巧回到本文開(kāi)頭,所有的函數(shù)/方法調(diào)用的時(shí)候都可以 轉(zhuǎn)換 為 call 形式,call 的第一個(gè)參數(shù)顯式的指明了函數(shù)該次執(zhí)行時(shí)候的上下文,這就是判斷 this 指向的技巧,以代碼為例進(jìn)行演示:
舉例一:
function foo () { console.log(this); } foo(); // window foo.call(); // window // non-strict mode foo.call(undefined); // window // strict mode foo.call(undefined); // undefined
foo() 為簡(jiǎn)單調(diào)用,因此 this === window 。
foo.call() 中,call() 的第一個(gè)參數(shù)未指明,那么 this === window ,在全局上下文中,非嚴(yán)格模式 下,undefined 即為 window ,嚴(yán)格模式 下,undefined 不能指代 window ,所以嚴(yán)格模式下 this === undefined 。
舉例二:
let obj = { fn1: function () { console.log(this === obj); } }; obj.fn1(); // true obj.fn1.call(obj); // true
舉例三:
let obj = { fn1: { fn2:function () { console.log(this === obj.fn1); } } }; obj.fn1.fn2(); // true obj.fn1.fn2.call(obj.fn1); // true
舉例四:
let obj = { fn1: function () { return function () { console.log(this === window); } } }; let fn2 = obj.fn1(); fn2(); // true fn2.call(); // true obj.fn1.call(obj).call(undefined); // true
以上三個(gè)例子中,如何判斷傳給 call() 的 this 呢?以舉例四的最后一句代碼為例進(jìn)行分析:
通過(guò)這張 call() 的圖解,this 應(yīng)該完全掌握了,所以將函數(shù)的調(diào)用改寫(xiě)為 call() 形式是最直接明了判斷 this 的方法。
看到這里,你搞懂 this 了嗎?
參考鏈接:
this - JavaScript | MDN
this 的值到底是什么?一次說(shuō)清楚
你怎么還沒(méi)搞懂 this ?
更多精彩內(nèi)容,請(qǐng)點(diǎn)擊我的博客 → The story of Captain
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/92846.html
摘要:流的類(lèi)型中有四種基本的流類(lèi)型可讀的流例如可寫(xiě)的流例如可讀寫(xiě)的流例如在讀寫(xiě)過(guò)程中可以修改和變換數(shù)據(jù)的流例如可讀流可讀流有兩種模式流動(dòng)模式可讀流自動(dòng)讀取數(shù)據(jù),通過(guò)接口的事件盡快將數(shù)據(jù)提供給應(yīng)用。 流的簡(jiǎn)介 流(stream)在 Node.js 中是處理流數(shù)據(jù)的抽象接口(abstract interface)。 stream 模塊提供了基礎(chǔ)的 API 。使用這些 API 可以很容易地來(lái)構(gòu)建實(shí)...
摘要:原生寫(xiě)的輪播兼容移動(dòng)端插件,支持輪播速度,輪播內(nèi)容,輪播間隔,手勢(shì)靈敏度自定義,導(dǎo)航圓點(diǎn)點(diǎn)擊跳轉(zhuǎn)手勢(shì)滑動(dòng)。使用說(shuō)明文件包含小部分語(yǔ)法編寫(xiě)的文件,在移動(dòng)端有兼容性問(wèn)題,僅供于源碼參考。移動(dòng)端跟端開(kāi)發(fā)引用文件直接下載進(jìn)行引入使用。 slide.js 原生js寫(xiě)的輪播兼容 pc+移動(dòng)端 插件,支持輪播速度,輪播內(nèi)容,輪播間隔,手勢(shì)靈敏度自定義,導(dǎo)航圓點(diǎn)點(diǎn)擊跳轉(zhuǎn),手勢(shì)滑動(dòng)。 使用說(shuō)明:sli...
摘要:綁定輪播事件然后是鼠標(biāo)移入移出事件的綁定鼠標(biāo)移入移出事件移入時(shí)停止輪播播放的定時(shí)器,移出后自動(dòng)開(kāi)始下一張的播放。 通過(guò)上一篇文章的學(xué)習(xí),我們基本掌握了一個(gè)輪子的封裝和開(kāi)發(fā)流程。那么這次將帶大家開(kāi)發(fā)一個(gè)更有難度的項(xiàng)目——輪播圖,希望能進(jìn)一步加深大家對(duì)于面向?qū)ο蟛寮_(kāi)發(fā)的理解和認(rèn)識(shí)。 So, Lets begin! 目前項(xiàng)目使用 ES5及UMD 規(guī)范封裝,所以在前端暫時(shí)只支持標(biāo)簽的引入方式...
摘要:本文包括簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu)和查找算法,屬于個(gè)人整理。初學(xué)編程可以用這里的東西聯(lián)系一下,看一看也挺有意思博主個(gè)人不認(rèn)為中算法數(shù)據(jù)結(jié)構(gòu)不重要,畢竟這是程序開(kāi)發(fā)的基本功。 本文包括簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu)和查找算法,屬于個(gè)人整理。初學(xué)編程可以用這里的東西聯(lián)系一下,看一看也挺有意思博主個(gè)人不認(rèn)為js中算法數(shù)據(jù)結(jié)構(gòu)不重要,畢竟這是程序開(kāi)發(fā)的基本功。本文還會(huì)根據(jù)博主學(xué)習(xí)進(jìn)展和時(shí)間安排不定期更新 數(shù)據(jù)結(jié)構(gòu)部分 列...
摘要:手勢(shì)解鎖界面一些對(duì)安全要求比較高的少不了鎖屏頁(yè)面,而手勢(shì)解鎖對(duì)于用戶(hù)來(lái)說(shuō)使用方便,對(duì)于程序員來(lái)說(shuō)小有挑戰(zhàn),怎么有棄之不用的道理。 ionic 2+ 手勢(shì)解鎖界面 一些對(duì)安全要求比較高的app少不了鎖屏頁(yè)面,而手勢(shì)解鎖對(duì)于用戶(hù)來(lái)說(shuō)使用方便,對(duì)于程序員來(lái)說(shuō)小有挑戰(zhàn),怎么有棄之不用的道理。 效果圖 效果圖處理短,方便大家閱讀showImg(https://segmentfault.co...
閱讀 3766·2021-11-11 11:02
閱讀 3496·2021-10-11 10:57
閱讀 3608·2021-09-22 16:00
閱讀 1843·2021-09-02 15:15
閱讀 1322·2019-08-30 15:56
閱讀 1005·2019-08-30 15:54
閱讀 2731·2019-08-30 12:43
閱讀 3539·2019-08-29 16:06