轉眼前端的學習已有一年,平常寫代碼中常常碰到this這個東西。特別是在用vue的時候,this仍是有點多的,哈哈。
在翻閱了一部分書籍和一堆大佬的博客後,決定總結一下這些東西,下面談談我對this的一些理解,若是有錯誤,歡迎你們批評指正。若是能夠給你帶來一些幫助,那是再好不過的了。前端
this是在運行時基於函數的執行環境綁定的,它的上下文取決於函數調用時的各類條件。
當一個函數被調用時,會建立一個活動記錄(有時候也稱執行上下文)。這個記錄會包含函數在哪裏被調用,函數的調用方法、出今年入的參數等信息。this就是記錄其中一個屬性,會在函數執行的過程當中用到 --你不知道的JavaScript(上)
var name = 'window' const obj1 = { name:'obj1', fn(){ console.log(this.name) } } obj1.fn()//'obj1'
var name = 'window' const obj1 = { name:'obj1', fn(){ // "use strict" console.log(this.name) } } const obj2 = { name:'obj2', fn2:obj1.fn } obj2.fn2()//'obj2' obj1.fn()//'obj1'
#this的綁定與函數聲明的位置沒有聲明的位置沒有任何關係,只取決於函數的調用方式
#通常來講,誰最終調用了函數,那麼它就是this的綁定對象。
那在全局下調用函數,this的綁定對象也就是全局對象vue
var name = 'window' function fn(){ console.log(this.name) } fn()//'window'
下面這種狀況爲何不是輸出window呢?es6
var name = 'window' function fn(){ this.name = 'fn' console.log(this.name) } fn()// 'fn'
也是由於在全局調用這個函數的時候,this.name === window.name就把以前的值給覆蓋了,因此輸出就是 'fn'。app
var name = 'window' const obj1 = { name:'obj1', fn(){ // "use strict" console.log(this.name) } } const obj2 = { name:'obj2', fn2:obj1.fn } obj2.fn2()//'obj2' obj1.fn()//'obj1'
仍是按照上面的代碼爲例,這裏函數
obj2.fn2()//徹底等價於下面的 obj2.fn2.call(obj2) obj1.fn() 等於 obj1.fn.call(obj1)
一樣的學習
var name = 'window' function fn(){ this.name = 'fn' console.log(this.name) } fn() 等於 fn.call()
var name = 'window' function fn(){ 'use strict' //爲函數開啓嚴格模式 this.name = 'fn' console.log(this.name) } fn() //則會報錯,這也一樣說明了函數在js內部調用的形式
改改前面的代碼this
var name = 'window' const obj1 = { name:'obj1', fn(){ this.name = 'fn' function inFn(){ console.log(this.name) } inFn() } } obj1.fn()//'window '
上面的inFn()也等同於inFn.call(),在全局調用它,天然也就是輸出window了。code
var name = 'window' const obj1 = { name:'obj1', fn(){ this.name = 'fn' inFn= ()=>{ console.log(this.name) } inFn() } } obj1.fn()//'fn'
它的this就是上一層第一個包裹它的普通函數的this
因此用call/bind/apply調用箭頭函數的時候,是沒有效果的。對象
var name = 'window' const obj1 = { name:'obj1', fn(){ this.name = 'fn' inFn= ()=>{ console.log(this.name) } inFn() } } obj1.fn.call(window)//'fn'
這也是爲何在es6後,諸如_this=this這類寫愈來愈少的緣由ip
setTimeout()調用的代碼運行在與所在函數徹底分離的執行環境上。這會致使,這些代碼中包含的 this 關鍵字在非嚴格模式會指向 window (或全局)對象,嚴格模式下爲 undefined,這和所指望的this的值是不同的。--MDN
備註:在嚴格模式下,setTimeout( )的回調函數裏面的this仍然默認指向window對象, 並非undefined
this.name = 'window' function Foo(){ this.name = 'Foo' this.fn = function(){ console.log(this.name) } this.timer = function(){ setTimeout(this.fn) } } var obj = new Foo() obj.timer()//'window'
可使用call/apply/bind調用改變this綁定對象。
this.name = 'window' function Foo(){ this.name = 'Foo' this.fn = function(){ console.log(this.name) } this.timer = function(){ setTimeout(this.fn.call(this)) } } var obj = new Foo() obj.timer()//'Foo'
還有箭頭函數
this.name = 'window' const obj = { name:'obj', fn(){ console.log(1) setTimeout(()=>{ console.log(this.name) },1000) } } obj.fn()//'obj'
this.name = 'window' function Foo(){ this.name = 'Foo' this.fn = function(){ console.log(this.name) } this.timer = function(){ setTimeout(this.fn) } } var obj = new Foo() obj.timer() //'window'定時器
1.箭頭函數
function Foo(){ this.name='Foo' this.fn = ()=>{ console.log(this.name) } } var foo = new Foo() this.name = 'window' //設置一下window.name的值 foo.fn() //'Foo' 輸出的是實例變量 console.log(this.name)//'window' 並無改變window.name的值
2.call/bind/apply
this.name = 'window' function Foo(){ this.name='Foo' this.fn = ()=>{ console.log(this.name) } } var foo = new Foo() foo.fn.call(window)//'Foo' const tes = foo.fn.bind(window) tes() //'Foo'
##也就是說判斷一個運行中的this綁定,就要找到這個函數的直接調用位置。 ##按照上面介紹的優先級順序進行判斷即可以找到this的綁定對象。