關(guān)于 this 指向私股,可能有一部分人都是模糊的茵宪,本文對(duì)常見情況下的 this 指向作出總結(jié)瞳别,讓你不再皺眉生宛。
先了解
一個(gè)基本概念:普通函數(shù)的 this 指向不是在定義的時(shí)候確定萍鲸,而是在調(diào)用的時(shí)候確定味咳。
兩個(gè)注意事項(xiàng):
- 所有例子在瀏覽器環(huán)境(window 對(duì)象同時(shí)也是全局對(duì)象)運(yùn)行庇勃,而不是 node 環(huán)境(global)。
-
非嚴(yán)格模式指向 Window 的對(duì)應(yīng)嚴(yán)格模式是 undefined槽驶。(
Window.setTimeout
等除外责嚷,由setTimeout
調(diào)用的代碼運(yùn)行在與所在函數(shù)完全分離的執(zhí)行環(huán)境上,即使在嚴(yán)格模式下仍然指向 Window)
接下來(lái)從一般形式函數(shù)調(diào)用掂铐、方法調(diào)用罕拂、apply 和 call 調(diào)用、箭頭函數(shù)全陨、class 等理清指向問(wèn)題爆班。
1. 一般形式函數(shù)調(diào)用
所謂一般形式函數(shù)調(diào)用就是 函數(shù)名()
,this 指向全局對(duì)象辱姨。
function test() {
console.log(this.name) // fang
}
var name = 'fang'
// let蛋济、const 聲明的變量不是 Window 屬性
const age = 1
console.log(this) // Window
console.log(this.age) // undefined
test()
2. 方法調(diào)用
一個(gè)函數(shù)被設(shè)置為對(duì)象(非全局對(duì)象)的屬性值時(shí),就是方法調(diào)用炮叶,this 指向?qū)ο笞陨怼?/p>
-
當(dāng)函數(shù)屬于對(duì)象外層屬性的屬性值
function test() { 'use strict' console.log(this) // obj console.log(this.name) // fang } var name = 'wang' const obj = { name: 'fang', fun: test } obj.fun()
-
當(dāng)函數(shù)屬于深層對(duì)象的屬性值碗旅,要明確知道它的使用者是誰(shuí)
function test() { console.log(this) // obj2 console.log(this.name) // zhang } var name = 'wang' const obj1 = { name: 'fang', obj2: { name: 'zhang', fun: test } } obj1.obj2.fun()
-
當(dāng)把對(duì)象的屬性值函數(shù)賦值給一個(gè)新的變量,就會(huì)變成一般形式函數(shù)調(diào)用
function test() { console.log(this) // Window console.log(this.name) // wang } var name = 'wang' const obj1 = { name: 'fang', obj2: { name: 'zhang', fun: test } } var t = obj1.obj2.fun t()
-
當(dāng)屬性值函數(shù)內(nèi)部還有函數(shù)
function test() { console.log(this) // Window console.log(this.name) // wang } var name = 'wang' const obj = { name: 'fang', foo: function() { console.log(this) // obj test() // test 是一般形式函數(shù)調(diào)用 } } obj.foo() // foo 是方法調(diào)用
3. call镜悉、apply 和 bind 用來(lái)改變 this 指向
-
call:
fun.call(thisArg[, arg1[, arg2[, ...]]])
祟辟,第一個(gè)參數(shù)是要綁定給 this 的值,后面?zhèn)魅雲(yún)?shù)列表侣肄。const obj = { name: 'fang' } function a() { console.log(this) } a.call(obj) // obj a.call(null) // 如果第一個(gè)參數(shù)是 null旧困、undefined,指向 Window(下同)
-
apply:
fun.apply(thisArg[, [arg1, arg2, ...]])
,可接收兩個(gè)參數(shù)吼具,第一個(gè)是綁定給 this 的值僚纷,第二個(gè)是數(shù)組。const obj = { name: 'fang' } function a() { console.log(this) } a.apply(obj) // obj a.apply(undefined) // Window
-
bind:
fun.bind(thisArg[, arg1[, arg2[, ...]]])
拗盒,與call
相似怖竭,但是返回的是一個(gè)函數(shù),需要手動(dòng)去執(zhí)行陡蝇。const obj1 = { name: 'fang' } const obj2 = { name: 'wang' } function a() { console.log(this) } const b = a.bind(obj1) const c = a.bind(obj2) // 需要手動(dòng)執(zhí)行 b() // obj1 a() // Window痊臭,函數(shù)a不會(huì)受影響 c() // obj2 b.call(null) // obj1(綁定關(guān)系一旦確認(rèn)給新變量,新變量繼續(xù)使用 call登夫、apply广匙、bind 不會(huì)再次改變指向) c.bind(obj2)() // obj2
4. 箭頭函數(shù)
箭頭函數(shù)沒(méi)有自己的 this,看其定義時(shí)外層是否有函數(shù)恼策,如果有鸦致,外層函數(shù)的 this 就是內(nèi)部箭頭函數(shù)的 this,如果沒(méi)有涣楷,則 this 指向 Window蹋凝。
-
箭頭函數(shù)外層沒(méi)有函數(shù)
const arrow = () => { console.log(this.name) // wang } var name = 'wang' const obj = { name: 'fang', foo: function() { console.log(this) // obj arrow() // 看定義時(shí) } } obj.foo()
var name = 'wang' const obj1 = { name: 'fang', obj2: { name: 'zhang', fun: () => { console.log(this) // Window } } } obj1.obj2.fun()
-
箭頭函數(shù)外層有函數(shù),注意外層函數(shù)的 this 指向按照之前規(guī)則判斷
var name = 'wang' const obj = { name: 'fang', foo: function() { console.log(this) // obj const arrow = () => { console.log(this) // 指向外層函數(shù) this } arrow() } } obj.foo()
-
箭頭函數(shù)能否被改變指向总棵?
var name = 'wang' const obj1 = {name: 'zhang'} const obj2 = { name: 'fang', foo: function() { console.log(this) // obj2 const arrow = () => { console.log(this.name) // fang } arrow.call(obj1) // 箭頭函數(shù)不會(huì)改變 this 指向 } } obj2.foo()
5. class 類(es6 嚴(yán)格模式)
-
創(chuàng)建類實(shí)例后鳍寂,再去調(diào)用類的方法,this 指向?qū)嵗龑?duì)象
class A { constructor({ age, name }) { this.name = name this.age = age } test() { console.log(this) // {name:'fang', age:1} } } const a = new A({ age: 1, name: 'fang' }) a.test()
-
直接通過(guò) prototype 對(duì)象調(diào)用 test情龄,指向 prototype
class A { constructor({ age, name }) { this.name = name this.age = age } test() { console.log(this) } } const a = new A({ age: 1, name: 'fang' }) a.test() // {name:'fang', age:1} console.log(A.prototype) // {constructor:f, test:f} A.prototype.test() // prototype(想想看是不是可以理解為方法調(diào)用)
-
子類創(chuàng)建一個(gè)實(shí)例后迄汛,指向子類實(shí)例對(duì)象,包括子類調(diào)用父類的方法
class A { constructor() { this.name = 1 this.age = 1 this.sex = 0 } test1(){ console.log(this) } } class B extends A { constructor({name,age}) { super() // super 必須置于 this 前 this.name = name // 如果不寫骤视,繼承父類的屬性 1 this.age = age // 如果不寫鞍爱,繼承父類的屬性 1 } test2() { console.log(this) } } const b = new B({ name: 3, age: 3 }) b.test2() // {age:3,name:3,sex:0} // 父類的方法被子類調(diào)用 b.test1() // {age:3,name:3,sex:0}
-
子類通過(guò) prototype 調(diào)用的指向與父類是有區(qū)別的
class A { constructor() { this.name = 1 this.age = 1 this.sex = 0 } test1(){ console.log(this) } } class B extends A { constructor({name,age}) { super() // super 必須置于 this 前 this.name = name // 如果不寫,繼承父類的屬性 1 this.age = age // 如果不寫专酗,繼承父類的屬性 1 } test2() { console.log(this) } } console.log(B.prototype) // A {constructor: ?, test2: ?} 注意與父類prototype的區(qū)別 B.prototype.test1() // A {constructor: ?, test2: ?} B.prototype.test2() // A {constructor: ?, test2: ?}
6. vue 中的 this
一般來(lái)說(shuō)睹逃,在 vue 生命周期函數(shù)或自定義方法中 this 指向的是 vue 實(shí)例,但是要注意下面的3種情況祷肯。
-
回調(diào)函數(shù) then 鏈?zhǔn)綄懛ㄓ闷胀ê瘮?shù)沉填,this 指向 undefined,可使用
_this=this
獲取 vue 實(shí)例let _this = this // vue實(shí)例 /* eslint-disable */ request().then( function (res){ console.log(this) // undefined console.log(_this) // vue實(shí)例 })
-
setTimeout 執(zhí)行普通函數(shù)指向 Window 佑笋,可使用箭頭函數(shù)獲取 vue 實(shí)例
setTimeout(() => { console.log(this) // vue 實(shí)例 }, 2000)
-
不應(yīng)該使用箭頭函數(shù)來(lái)定義 method 函數(shù)翼闹,箭頭函數(shù)綁定了父級(jí)作用域的上下文,this 指向 undefined蒋纬。
methods: { todo: () => console.log(this) // undefined }
以上理解如果有不對(duì)之處請(qǐng)指出猎荠。