參考: http://www.ruanyifeng.com/blog/2018/06/javascript-this.html
this在非箭頭函數下始終指向當前調用的對象。
1、爲什麼函數內部要設置this?
var obj = {
foo: function () { console.log(this.bar) },
bar: 1
};
var foo = obj.foo;
var bar = 2;
obj.foo() // 1
foo() // 2
this指的是函數運行時所在的環境,那麼函數的運行環境到底是怎麼決定的?爲什麼obj.foo()就是在obj環境執行,而一旦var foo = obj.foo,foo()就變成在全局環境執行?主要和數據在內存中存儲有關。
var obj = { foo: 5 };
上面的代碼將一個對象賦值給變量obj。JS引擎會先在堆內存裏生成一個對象{ foo: 5 },然後把這個對象的內存地址賦值給變量obj。如果要讀取obj.foo,引擎先從obj拿到內存地址,然後再從該地址讀出原始的對象,返回它的foo屬性。
var obj = { foo: function () {} };
這時,引擎會將函數單獨保存在內存中,然後再將函數的地址賦值給foo屬性的value屬性。
此時,由於函數是一個單獨的值,所以它可以在不同的環境(上下文)執行。
var f = function () {
console.log(x);
};
JS允許在函數體內部,引用當前環境的其他變量。現在問題就來了,由於函數可以在不同的運行環境執行,所以需要有一種機制,能夠在函數體內部獲得當前的運行環境(context)。所以,this就出現了,它的設計目的就是在函數體內部,指代函數當前的運行環境。
var f = function () {
console.log(this.x);
}
上面代碼中,函數體裏面的this.x就是指當前運行環境的x。
var foo = function () {
console.log(this.x);
}
var x = 1;
var obj = {
foo: foo,
x: 2,
};
foo() // 1
obj.foo() // 2
回到本文開頭提出的問題,obj.foo()是通過obj找到foo,所以就是在obj環境執行。一旦var foo = obj.foo,變量foo就直接指向函數本身,所以foo()就變成在全局環境執行。
2、JS函數不同調用方式時this指向問題
函數調用方式主要分爲以下:
- 作爲對象方法調用(指向當前的調用對象,具體看上面部分)
- 作爲函數調用(非嚴格模型this指向window,嚴格模型指向undefined)
- 作爲構造函數調用(this指向其實例)
- 使用call或者applay調用(this指向第一個參數)
當函數作爲構造函數時,new一個構造函數的實例有以下幾個步驟:
- 創建一個空對象obj
- 設置原型鏈:obj.proto = 構造函數名.prototype
- 讓構造函數內部的this指向obj(默認returnt爲當前this,構造函數renturn一個對象時,this指向return的對象)調用構造函數中的屬性,執行函數中的代碼,爲obj添加屬性。
- 返回對象obj的地址
setTimeout有兩個參數,第一個是對象,第二個是間隔時間。由於setTimeout是window下的方法,那麼在函數內this指向window。
3、箭頭函數下的this
箭頭函數中this對象就是定義時所在的作用域,也就是說箭頭函數本身沒有this,內部的this就是外層代碼塊作用域中的this。
var a = 0
var obj = {
a: 1,
foo: ()=> {
console.log(this.a)
}
}
obj.foo() //0