● 在 javascript 語(yǔ)言中, 有一個(gè)奇奇怪怪的 "關(guān)鍵字" 叫做 this
● 為什么說(shuō)它是 奇奇怪怪 呢, 是因?yàn)槟銓?xiě)出 100 個(gè) this, 可能有 100 個(gè)解釋, 完全不挨邊
● 但是, 在你的學(xué)習(xí)過(guò)程中, 搞清楚了 this 這個(gè)玩意, 那么會(huì)對(duì)你的開(kāi)發(fā)生涯有很大幫助的
● 接下來(lái)咱們就開(kāi)始一點(diǎn)一點(diǎn)的認(rèn)識(shí)一下 this
this 初認(rèn)識(shí)
● 看到 this, 先給他翻譯過(guò)來(lái) "這個(gè)"
● 到底啥意思呢 ?
○ 飯桌上, 你媽和你說(shuō), 你多吃點(diǎn)的這個(gè)
○ 商店里, 你媳婦和你說(shuō), 這個(gè)包 這個(gè)包 這個(gè)包 我都要
○ 宴會(huì)上, 你爸和人介紹說(shuō), 這個(gè)傻小子是我兒子
● 你看, 每一句話上都有 "這個(gè)", 但是每個(gè) "這個(gè)" 都是一個(gè)意思嗎 ? 并不
● 就像我們 js 內(nèi)的 this 一樣, 每一個(gè) this 的意思都不一樣
● 但是我們會(huì)發(fā)現(xiàn)
○ 在說(shuō)話的過(guò)程中, "這個(gè)" 是和我們說(shuō)話的手勢(shì)有關(guān)系
● 在 js 內(nèi)一個(gè)道理
○ this 的意思是和代碼的 "手勢(shì)" 有關(guān)系
● 例子 :
○ 當(dāng)你媳婦手指著一個(gè) LV 包的時(shí)候, 說(shuō)的 "這個(gè)" 指代的就是 LV包`
○ 當(dāng)你媽指著魚(yú)香肉絲的時(shí)候說(shuō) "這個(gè)" 指代的就是 魚(yú)香肉絲
○ 所以在 javascript 內(nèi)的 this 是要看 "說(shuō)這句話的代碼手指向哪里了"
● 看看下面一段代碼
var box = document.querySelector('#box')
box.onclick = function () {
console.log(this)
}
○ 當(dāng)你點(diǎn)擊 box 這個(gè)元素的時(shí)候, 會(huì)觸發(fā)后面的函數(shù)
○ 然后函數(shù)一執(zhí)行, 就會(huì)在控制臺(tái)打印一下 this
○ 這里的 this 就是 box 這個(gè)元素
● 這就是一個(gè)非常簡(jiǎn)單的 this 指向的例子了
● 接下來(lái)我們就開(kāi)始詳細(xì)學(xué)習(xí)一下 this
給你個(gè)概念
● this , 是一個(gè)指針形變量, 它動(dòng)態(tài)的指向當(dāng)前函數(shù)的運(yùn)行環(huán)境
● "什么鬼東西, 我聽(tīng)不懂啊"
● 給一個(gè)私人的解釋 : "根據(jù) this 所在的函數(shù)是如何被調(diào)用的來(lái)決定 this 是什么"
● 舉個(gè)栗子來(lái)看一下
function fn() {
console.log(this)
}
fn()
// this 就是 window
● 因?yàn)?this 是在 fn 函數(shù)內(nèi), 所以 fn 函數(shù)的調(diào)用方式就決定了這個(gè) this 是什么
function a() {
function b() {
console.log(this)
}
b()
}
a()
// this 就是 window
○ 因?yàn)?this 是在 b 函數(shù)內(nèi), 所以 b 函數(shù)的調(diào)用方式?jīng)Q定了 this 是什么, 和 a 函數(shù)沒(méi)關(guān)系
● 就是這個(gè)意思
● 最后, 根據(jù)這些年的經(jīng)驗(yàn)總結(jié)給出一個(gè)私人的概念, 要牢記
○ 函數(shù)的 this
○ 和函數(shù)定義在哪沒(méi)關(guān)系
○ 和函數(shù)怎么定義沒(méi)關(guān)系
○ 只看這個(gè)函數(shù)的調(diào)用方式
○ 箭頭函數(shù)除外
對(duì)象調(diào)用
● 對(duì)象調(diào)用, 就是利用一個(gè)對(duì)象作為宿主來(lái)調(diào)用函數(shù)
● 最簡(jiǎn)單的方式就是把函數(shù)寫(xiě)在一個(gè)對(duì)象內(nèi), 利用對(duì)象來(lái)調(diào)用
// 對(duì)象內(nèi)寫(xiě)一個(gè)函數(shù)
const obj = {
fn: function () { console.log(this) }
}
// 調(diào)用這個(gè)函數(shù)
obj.fn()
○ 這時(shí)候, 我們調(diào)用了和這個(gè)對(duì)象內(nèi)的 fn 函數(shù)
○ 調(diào)用方式就是利用對(duì)象調(diào)用的函數(shù), 所以在這個(gè)函數(shù)內(nèi)的 this 就是 obj 這個(gè)對(duì)象
○ 換句話說(shuō), 只要在這個(gè)函數(shù)內(nèi), 只要出現(xiàn) this 就是這個(gè)對(duì)象
全局調(diào)用
● 顧名思義, 全局調(diào)用就是直接調(diào)用一個(gè)全局函數(shù)
function fn() {
console.log(this)
}
fn()
○ 此時(shí)這個(gè)函數(shù)內(nèi)的 this 就是 window
○ 可能有的小伙伴覺(jué)得瘋了
○ 但是我們仔細(xì)思考一下, 你會(huì)發(fā)現(xiàn)
○ 其實(shí) fn 因?yàn)槭窃谌稚系? 那么其實(shí)調(diào)用的完整寫(xiě)法可以寫(xiě)成 window.fn()
○ 此時(shí)就回到了之前對(duì)象調(diào)用那條路上, 這樣就通順了
奇怪的調(diào)用
● 這個(gè)時(shí)候, 有的小伙伴可能會(huì)想到一個(gè)問(wèn)題, 如果這個(gè)函數(shù)不放在全局呢 ?
const obj = {
fn: function () {
function fun() {
console.log(this)
}
fun()
}
}
obj.fn()
● 此時(shí)的 this 應(yīng)該是什么呢 ?
● 按照之前的思路思考
○ obj.fn() 確實(shí)調(diào)用了函數(shù), 但是 this 不是在 obj.fn 函數(shù)內(nèi), 是在 fun 函數(shù)內(nèi)
○ fun() 確實(shí)也調(diào)用了函數(shù), 但是我沒(méi)有辦法寫(xiě)成 window.fun()
○ 那么 this 到底是不是 window 呢, 還是應(yīng)該是 obj 內(nèi)
● 答案確實(shí)是 window, 這又是為什么呢 ?
捋一下思路
● 說(shuō)道這里, 我們會(huì)發(fā)現(xiàn)
● this 真的是好奇怪哦 o(* ̄︶ ̄*)o 搞不定了
● 要是按照這個(gè)方式, 我來(lái)回來(lái)去的得記多少種, 誰(shuí)會(huì)記得下來(lái)呢
● 接下來(lái)(劃重點(diǎn))
我用我代碼三十年的經(jīng)驗(yàn)給你總結(jié)出來(lái)了一些內(nèi)容, 希望你能牢記
this 的個(gè)人經(jīng)驗(yàn)
● 首先, this 在各種不同的情況下會(huì)不一樣
● 那么從現(xiàn)在開(kāi)始我把我總結(jié)的內(nèi)容毫無(wú)保留的傳授給你
經(jīng)驗(yàn)一 :
● 在 js 的非嚴(yán)格模式下適用
● 在非箭頭函數(shù)中適用
● 不管函數(shù)定義在哪, 不管函數(shù)怎么定義, 只看函數(shù)的調(diào)用方式
○ 只要我想知道 this 是誰(shuí)
○ 就看這個(gè) this 是寫(xiě)在哪個(gè)函數(shù)里面
○ 這個(gè)函數(shù)是怎么被調(diào)用的
觀察 this 在哪個(gè)函數(shù)內(nèi)
function fn() {
console.log(this)
}
// this 在函數(shù) fn 內(nèi), 就看 fn 函數(shù)是怎么被調(diào)用的就能知道 this 是誰(shuí)
const obj = {
fn: function () {
console.log(this)
}
}
// this 在 obj.fn 函數(shù)內(nèi), 就看這個(gè)函數(shù)怎么被調(diào)用的就能知道 this 是誰(shuí)
const obj = {
fn: function () {
function fun() {
console.log(this)
}
}
}
// 這個(gè) this 是在 fun 函數(shù)內(nèi)
// 如果你想知道這個(gè) this 是誰(shuí)
// 和 obj.fn 函數(shù)沒(méi)有關(guān)系, 只要知道 fun 函數(shù)是怎么被調(diào)用的就可以了
● 一定要注意 : 你想知道的 this 在哪個(gè)函數(shù)內(nèi), 就去觀察哪個(gè)函數(shù)的調(diào)用方式就好了
一些常見(jiàn)的函數(shù)調(diào)用方式
1. 普通調(diào)用
● 調(diào)用方式 : 函數(shù)名()
● this 是 window
● 只要你書(shū)寫(xiě) "函數(shù)名()" 調(diào)用了一個(gè)函數(shù), 那么這個(gè)函數(shù)內(nèi)的 this 就是 window
function fn() {
console.log(this)
}
fn()
// 這里就是 fn() 調(diào)用了一個(gè)函數(shù), 那么 fn 內(nèi)的 this 就是 window
const obj = {
fn: function () {
function fun() {
console.log(this)
}
fun()
}
}
obj.fn()
// 這里的 this 因?yàn)槭窃?fun 函數(shù)內(nèi)
// fun() 就調(diào)用了這個(gè) fun 函數(shù)
// 所以不用管 fun 函數(shù)寫(xiě)在了哪里
// 這個(gè) fun 函數(shù)內(nèi)的 this 就是 window
2. 對(duì)象調(diào)用
● 調(diào)用方式:
○ 對(duì)象.函數(shù)名()
○ 對(duì)象['函數(shù)名']()
● this 就是這個(gè)對(duì)象, 對(duì)象叫啥, 函數(shù)內(nèi)的 this 就叫啥
const obj = {
fn: function () {
console.log(this)
}
}
obj.fn()
// 因?yàn)?obj.fn() 調(diào)用了這個(gè)函數(shù), 所以 obj.fn 函數(shù)內(nèi)的 this 就是 obj
const xhl = {
fn: function () {
console.log(this)
}
}
xhl.fn()
// 因?yàn)?obj.fn() 調(diào)用了這個(gè)函數(shù), 所以 xhl.fn 函數(shù)內(nèi)的 this 就是 xhl
function fn() {
const xhl = {
fn: function () {
console.log(this)
}
}
xhl.fn()
}
fn()
// 因?yàn)槲覀円^察的 this 是在 xhl.fn 這個(gè)函數(shù)內(nèi)
// 所以只需要關(guān)注這個(gè)函數(shù)是如何被調(diào)用的即可
// 因?yàn)槭?xhl.fn 調(diào)用了和這個(gè)函數(shù), 所以函數(shù)內(nèi)的 this 就是 xhl
3. 定時(shí)器調(diào)用
● 調(diào)用方式
○ setTimeout(function () {}, 1000)
○ setInterval(function () {}, 1000)
● this 就是 window
● 一個(gè)函數(shù)不管是怎么定義的, 只要被當(dāng)做定時(shí)器處理函數(shù)使用, this 就是 widnow
setTimeout(function () {
console.log(this)
}, 1000)
// 這里的 this 就是 window
setInterval(function () {
console.log(this)
}, 1000)
// 這里的 this 就是 window
const xhl = {
fn: function () {
console.log(this)
}
}
setTimeout(xhl.fn, 1000)
// 這里的 xhl.fn 函數(shù)不是直接書(shū)寫(xiě) xhl.fn() 調(diào)用的
// 而是給到了 setTimeout 定時(shí)器處理函數(shù)
// 所以這里的 this 就是 window
4. 事件處理函數(shù)
● 調(diào)用方式
○ 事件源.on事件類型 = 事件處理函數(shù)
○ 事件源.addEventListener(事件類型, 事件處理函數(shù))
● this 就是 事件源
● 只要是作為事件處理函數(shù)使用, 那么該函數(shù)內(nèi)的 this 就是 事件源
奧,對(duì)了,事件就是:在事件中,當(dāng)前操作的那個(gè)元素就是事件源
box.onclick = function () {
console.log(this)
}
// 這里的 this 就是 box
box.addEventListener('click', function () {
console.log(this)
})
// 這里的 this 就是 box
const xhl = {
fn: function () {
console.log(this)
}
}
box.addEventListener('click', xhl.fn)
// 這里的 xhl.fn 函數(shù)不是直接書(shū)寫(xiě) xhl.fn() 調(diào)用的
// 而是給到了 事件, 被當(dāng)做了事件處理函數(shù)使用
// 所以這里的 this 就是 事件源box
const xhl = {
fn: function () {
console.log(this)
}
}
box.onclick = xhl.fn
// 這里的 xhl.fn 函數(shù)不是直接書(shū)寫(xiě) xhl.fn() 調(diào)用的
// 而是給到了 事件, 被當(dāng)做了事件處理函數(shù)使用
// 所以這里的 this 就是 事件源box
5. 構(gòu)造函數(shù)調(diào)用
● 調(diào)用方式
○ new 函數(shù)名()
● this 就是該構(gòu)造函數(shù)的當(dāng)前實(shí)例
● 只要和 new 關(guān)鍵字調(diào)用了, this 就是實(shí)例對(duì)象
function fn() {
console.log(this)
}
const f = new fn()
// 這里的因?yàn)?fn 函數(shù)和 new 關(guān)鍵字在一起了
// 所以這里的 this 就是 fn 函數(shù)的實(shí)例對(duì)象
// 也就是 f
const xhl = {
fn: function () {
console.log(this)
}
}
const x = new xhl.fn()
// 這里的 xhl.fn 也是因?yàn)楹?new 關(guān)鍵字在一起了
// 所以這里的 this 就是 xhl.fn 函數(shù)的實(shí)例對(duì)象
// 也就是 x
記清楚原則 :
不管函數(shù)在哪定義
不管函數(shù)怎么定義
只看函數(shù)的調(diào)用方式
經(jīng)驗(yàn)二 :
● 在嚴(yán)格模式下適用
● 其實(shí)只有一個(gè)
○ 全局函數(shù)沒(méi)有 this, 是 undefined
○ 其他的照搬經(jīng)驗(yàn)一就可以了
1. 非嚴(yán)格模式
// 非嚴(yán)格模式
function fn() {
console.log(this)
}
fn()
// 因?yàn)槭窃诜菄?yán)格模式下, 這里的 this 就是 window
2. 嚴(yán)格模式
// 嚴(yán)格模式
'use strict'
function fn() {
console.log(this)
}
fn()
// 因?yàn)槭窃趪?yán)格模式下, 這里的 this 就是 undefined
記清楚原則 :
嚴(yán)格模式下
全局函數(shù)沒(méi)有 this
是個(gè) undefiend
經(jīng)驗(yàn)三 :
● 專門來(lái)說(shuō)一下箭頭函數(shù)
● 其實(shí)也只有一條
○ 推翻之前的所有內(nèi)容
○ 箭頭函數(shù)內(nèi)沒(méi)有自己的 this
○ 箭頭函數(shù)內(nèi)的 this 就是外部作用域的 this
● 換句話說(shuō), 當(dāng)你需要判斷箭頭函數(shù)內(nèi)的 this 的時(shí)候
○ 和函數(shù)怎么調(diào)用沒(méi)有關(guān)系了
○ 要看函數(shù)定義在什么位置
// 非箭頭函數(shù)
const xhl = {
fn: function () {
console.log(this)
}
}
xhl.fn()
// 因?yàn)槭?非箭頭函數(shù), 所以這里的 this 就是 xhl
// ==========================================================
// 箭頭函數(shù)
const xhl = {
fn: () => {
console.log(this)
}
}
xhl.fn()
// 因?yàn)槭?箭頭函數(shù), 之前的經(jīng)驗(yàn)不適用了
// 這個(gè)函數(shù)外部其實(shí)就是全局了, 所以這里的 this 就是 window
// 非箭頭函數(shù)
box.onclick = function () {
console.log(this)
}
// 因?yàn)槭?非箭頭函數(shù), 這里的 this 就是 box
// ==========================================================
// 箭頭函數(shù)
box.onclick = () => {
console.log(this)
}
// 因?yàn)槭?箭頭函數(shù)
// 這個(gè)函數(shù)外部就是全局了, 所以這里的 this 就是 window
// 非箭頭函數(shù)
const obj = {
fn: function () {
function fun() {
console.log(this)
}
fun()
}
}
obj.fn()
// 因?yàn)槭?非箭頭函數(shù), 所以 fun 函數(shù)內(nèi)的 this 就是 window
// ==========================================================
// 箭頭函數(shù)
const obj = {
fn: function () {
const fun = () => {
console.log(this)
}
fun()
}
}
obj.fn()
// 因?yàn)槭?箭頭函數(shù)
// 那么這個(gè) fun 外面其實(shí)就是 obj.fn 函數(shù)
// 所以只要知道了 obj.fn 函數(shù)內(nèi)的 this 是誰(shuí), 那么 fun 函數(shù)內(nèi)的 this 就出來(lái)了
// 又因?yàn)?obj.fn 函數(shù)內(nèi)的 this 是 obj
// 所以 fun 函數(shù)內(nèi)的 this 就是 obj
記清楚原則 :
只要是箭頭函數(shù)
不管函數(shù)怎么調(diào)用
就看這個(gè)函數(shù)定義在了哪里
最后
● 好了
● 按照以上三個(gè)經(jīng)驗(yàn), 記清楚原則
● 那么在看到 this 就不慌了