this
關鍵字是JavaScript
中最複雜的機制之一。以致於我使用多年也很難說清它到底指向什麼,咱們有必要好好學習一下。this
也是咱們學習和使用JavaScript
中的一座大山,咱們必須翻過這座山。java
先看一段代碼以下:函數
var foo = {
name:'tom',
age:'24',
say:function(){
console.log('name: ' + this.name + ', age: ' + this.age);
}
}
foo.say(); //name: tom, age: 24
var bar = {
name:'cat',
age:'25'
}
foo.say.call(bar); //name: cat, age: 25
複製代碼
這段代碼在不一樣的上下文對象(foo
和bar
)中重複使用了函數say()
,不用寫不一樣版本的函數。若是不使用this
那麼就須要給函數say()
顯示傳入一個上下文對象。學習
say:function(context){
console.log('name: ' + context.name + ', age: ' + context.age);
}
複製代碼
this
隱式的傳遞了一個對象的引用,由於咱們能夠將通用模塊和API方法設計的更加易用和簡潔。ui
在開始工做的時候很容易把this
理解成指向函數自身,不過咱們如今來分析下,this
是否是指向函數自身。思考下面一段代碼:this
function foo(){
this.name = 'tom';
}
foo.name = 'cat';
foo();
console.log(foo.name); //'cat'
複製代碼
最終打印的是cat
,顯然this
不是指向函數自身。執行foo.name=cat
,是向對象foo
添加一個name
屬性,可是函數內部代碼this.name
中的this
並非指向那個函數對象。spa
遇到這樣的問題,咱們會採用其餘的方式來達到目的,好比建立另外一個帶有name
屬性的對象。設計
function foo(){
obj.name = 'tom';
}
var obj = {
name:'cat'
}
foo();
console.log(obj.name); //'tom'
複製代碼
雖然這種方式確實解決了問題,其使用的是另外一種咱們比較熟悉的技術詞法做用域
,並無使用this
解決。3d
另外一種方法是強制this
指向foo
函數對象。code
function foo(){
this.name = 'tom';
}
foo.name = 'cat';
foo.call(foo);
console.log(foo.name);//'tom'
複製代碼
此次咱們沒有迴避this
,並且使用call()
來幫助咱們使用this
。cdn
this
的另外一個誤區就是,指向函數的做用域。這個問題有點複雜,由於在某種狀況下它是正確的,可是在其餘狀況下它倒是錯誤的。須要說明的是,this
在任何狀況下都不指向函數的詞法做用域。
在
javaScript
中,做用域確實和對象相似,可見的標識符都是它的屬性。可是做用域「對象」沒法經過javaScript
代碼訪問,它存在於javaScript
引擎內部。(詳細的能夠參考以前的文章)
思考下面一段代碼:
function foo() {
var a = 1;
this.bar();
}
function bar() {
console.log(this.a);
}
foo(); //undefined
複製代碼
這段代碼看起來像是故意爲之,但實則反映出對this
的不瞭解。調用bar()
最經常使用的方法是省略前面的this
,直接使用詞法引用標識符。這段代碼還試圖經過this
在foo()
函數中聯動bar()
函數訪問變量a
。這也是不可能的,不能使用this
來引用一個詞法做用域內部的變量及函數。
this
是在運行時進行綁定的,並非在編寫時綁定,它的上下文取決於函數調用時的各類條件。this
的綁定和函數聲明的位置沒有任何關係,只取決於函數的調用方式。
當一個函數被調用時,會建立一個執行環境(也叫執行上下文)。執行環境中的活動對象會包含函數在哪裏被調用(調用棧)、函數的調用方法、傳入的參數等信息。this
就是記錄的其中一個屬性,會在函數執行的過程當中用到。(詳細的能夠參考以前的文章)
this 其實是在函數被調用時發生的綁定,它指向什麼徹底取決於函數在哪裏被調用。