This in JavaScript
這篇文章我們來理解下js中的this
關鍵字
在開始之前,我們先定義幾個規則
-
函式作為方法呼叫, 也就是用
.
的方式 -
用
new
的方式 -
用
apply
,call
,bind
的方式 - 函式作為自由函式呼叫
第一種規則,函式作為方法呼叫
this
指向的就是呼叫方法的物件
let obj = { name: 'len', sayName: function() { console.log(this) } } obj.sayName() // { name: 'len', sayName: [Function: sayName] } 複製程式碼
第二種規則
this
指向的就是新建立的物件,我們都知道,使用new
關鍵字的時候,會建立一個新的物件
let obj = { name: 'len', sayName: function() { console.log(this) } } new obj.sayName() // sayName {} 複製程式碼
第三種規則
這時候,this
指向的就是呼叫bind,apply,call方法的第一個引數(下面例子中的obj
)
function fn() { console.log(this) } let obj = { value: 5 } let boundFn = fn.bind(obj) boundFn() // { value: 5 } fn.call(obj) // { value: 5 } fn.apply(obj) // { value: 5 } 複製程式碼
第四種規則
在瀏覽器中, 這時候this
指向的就是就是window物件
function foo() { console.log(this) } foo() // Window{} 複製程式碼
規則中的組合情況
const obj = { value: 'hi', printThis: function() { console.log(this) } } const print = obj.printThis // 規則1,this指向的就是obj obj.printThis()// {value: 'hi', printThis: f} // 規則4,this指向的就是Window print() // Window {} 複製程式碼
const obj1 = { value: 'hi', printThis: function() { console.log(this) } } const obj2 = {value: 17} // 規則1和3的組合, 規則3的優先順序大於規則1 obj1.print.call(obj2) // {value: 17} // 規則1和規則2的組合,規則2的優先順序大於規則1 new obj1.print() // {} 複製程式碼
閉包中的this
var name = 'window' let object = { name: 'len', sayName: function() { return function() { return this.name } } } console.log(object.sayName()()) // window 複製程式碼
上面為什麼列印window呢?我們拆開來看
let firsr = object.sayName() first() // 這是作為單獨函式呼叫的,也是就是上面的'規則4',所以this自然就指向window了 複製程式碼
es6箭頭函式的this
箭頭函式沒有自己的this,請看:
// es6 function foo() { setTimeout(() => { console.log(this) }) } // es5 function foo() { var _this = this setTimeout(function() { console.log(_this) }, 1000) } foo() // Window{} new foo() // foo{} 這樣就又回到了上面的規則了。 複製程式碼
上面es5程式碼是我用線上babel編譯es6程式碼得到的。從上面我們可以看出,箭頭函式是沒有自己的this的,它實際上是儲存了父級作用域的this,然後在箭頭函式裡面呼叫的都是父級作用域的this。也正是因為箭頭函式沒有this,所以自然而然我們就不能使用call,apply,bind來改變this的指向了。
最後,來個面試題
class Animal() { constructor(type) { this.type = type } say(val) { setTimeout(() => { console.log(this); console.log(this.type + " says " + val); }, 1000) } } // es6的class裡面的方法都是定義在prototype上的 console.log(Animal.prototype) // 在瀏覽器中 {constructor: f, say: f} const animal = new Animal('animal') animal.say('hi') // 1s later console log "Animal{type: 'animal'}" and "animal says hi" 複製程式碼
我們將上面的改動一下
class Animal() { constructor(type) { this.type = type } say(val) { // 這裡沒有使用箭頭函式,這裡的setTimeout形成了一個閉包,this指向的是window setTimeout(function() { console.log(this); console.log(this.type + " says " + val); }, 1000) } } const animal = new Animal('animal') animal.say('hi') // Window {} / undefined says hi 複製程式碼
總結: js中的this還是挺好理解。只要自己用心去理解,相信你可以做到的。好了,就到這了,如果覺得文章有什麼問題,可以在下面留言探討下。畢竟都是在學習,沒有不出錯的。謝謝理解!