js this 詳解
this詳解
你可能遇到過這樣的 JS 面試題:
{
var obj = {
foo: function () {
console.log(this)
}
}
var bar = obj.foo
obj.foo(); // 打印出的 this 是 obj
bar(); // 打印出的 this 是 window
}
這個原因是因為“Javascript中this永遠是指向呼叫它的物件”
函式呼叫
函式的三種呼叫方式
func(p1, p2) obj.child.method(p1, p2) func.call(context, p1, p2) // 先不講 apply
前面兩種是常見的呼叫方式,但是第三種呼叫形式,才是正常呼叫形式:
{
func(p1, p2)
// 等價於
func.call(undefined, p1, p2)
obj.child.method(p1, p2)
// 等價於
obj.child.method.call(obj.child, p1, p2)
}
其他兩種都是語法糖,可以等價地變為 call 形式:
至此我們的函式呼叫只有一種形式:
func.call(context, p1, p2)
這樣,this 就好解釋了
this,就是上面程式碼中的 context。就這麼簡單。
this 是你 call 一個函式時傳的 context,由於你從來不用 call 形式的函式呼叫,所以你一直不知道。
先看 func(p1, p2) 中的 this 如何確定:
當你寫下面程式碼時
{
function func() {
console.log(this)
}
func();
等價於
func.call(undefined) // 可以簡寫為 func.call()
}
按理說打印出來的 this 應該就是 undefined 了吧,但是瀏覽器裡有一條規則:
如果你傳的 context 就 null 或者 undefined,那麼 window 物件就是預設的 context(嚴格模式下預設 context 是 undefined)
因此上面的列印結果是 window。
如果你希望這裡的 this 不是 window,很簡單:
{
func.call(obj) // 那麼裡面的 this 就是 obj 物件了
}
再看 obj.child.method(p1, p2) 的 this 如何確定
{
var obj = {
foo: function () {
console.log(this)
}
}
obj.foo()
}
按照「轉換程式碼」,我們將 obj.foo() 轉換為
{
obj.foo.call(obj)
}
好了,this 就是 obj。搞定。
回到題目:
{
var obj = {
foo: function () {
console.log(this)
}
}
var bar = obj.foo
obj.foo() // 轉換為 obj.foo.call(obj),this 就是 obj
bar()
}
/轉換為 bar.call(); 由於沒有傳 context;所以 this 就是 undefined;最後瀏覽器給你一個預設的 this —— window 物件
語法
{
function fn() { console.log(this) }
var arr = [fn, fn2]
arr[0]() // 這裡面的 this 又是什麼呢?
}
我們可以把 arr[0]() 想象為arr.0() ,雖然後者的語法錯了,但是形式與轉換程式碼裡的 obj.child.method(p1, p2) 對應上了,於是就可以愉快的轉換了: arr[0](),假想為arr.0(),然後轉換為 arr.0.call(arr),那麼裡面的 this 就是 arr 了:)
總結:
this 就是你 call 一個函式時,傳入的 context。
如果你的函式呼叫形式不是 call 形式,請按照「轉換程式碼」將其轉換為 call 形式。
1 this的四種用法
在Javascript中this總是指向呼叫它所在方法的物件。因為this是在函式執行時,自動生成的一個內部物件,只能在函式內部使用。
1.1在一般函式方法中使用 this 指代全域性物件
{
function test() {
this.x = 1;
console.log(this.x);
console.log(this);
};
test();
console.log(window.x)
}
1.2作為物件方法呼叫,this 指代上級物件
{
function test() {
console.log(this.x);
console.log(this)
}
var o = {};
o.x = 1;
o.m = test;
o.m(); // 1
}
1.3作為建構函式呼叫,this 指代new 出的物件
{
function test() {
this.x = 1;
console.log(this);
}
var o = new test();
console.log(o.x); // 1
執行結果為1。為了表明這時this不是全域性物件,我對程式碼做一些改變:
var x = 2;
function test2() {
this.x = 1;
console.log(this);
}
var o = new test2();
console.log(x); //2
}
1.4 apply 呼叫 ,apply方法作用是改變函式的呼叫物件,此方法的第一個引數為改變後呼叫這個函式的物件,this指代第一個引數
{
var x = 0;
function test() {
console.log(this.x);
}
var o = {};
o.x = 1;
o.m = test;
o.m.apply(); //0
//apply()的引數為空時,預設呼叫全域性物件。因此,這時的執行結果為0,證明this指的是全域性物件。如果把最後一行程式碼修改為
o.m.apply(o); //1
}
{
function fn(num) {
console.log("fn: " + num);
// count用於記錄fn的被呼叫次數
this.count++;
console.log(this)
}
fn.count = 0;
var i;
for (i = 0; i < 10; i++) {
if (i > 5) {
fn(i);
}
}
console.log(fn.count); //0
console.log(this.count); //NaN
}
2.箭頭函式的簡單講解
資料上說的箭頭函式內部是沒有this的,也就是說,箭頭函式裡面的this會繼承自外部的this,下面有個例子,用來詳細說明下:
{
var x = 11;
var obj = {
x: 22,
y: this,//window
say: () => {
console.log(this.x);
}
}
obj.say();
//輸出的值為11
console.log(obj.y);
//輸出的值為window物件
}
obj物件中的this指代的就是window,也就是全域性環境,因為箭頭函式中的this就會就近找到上一個物件中this所指代的物件,
從以上例子可以看出來,obj內部屬性y就為obj內部this指代的物件,輸出是window。
看看下面這個例子
{
var a = 11
function test1() {
this.a = 22;
let b = function () {
console.log(this.a);
};
b();
}
var x = new test1();
//輸出11
}