[JS] 箭頭函式和一般函式 this 指向


Posted by Akira on 2024-01-30

範圍鏈 (Scope Chain),Javascript 在決定外部參考環境的時候是以「詞彙環境 (Lexical Environment)」為準則的。

let me = 'global'
a()
function a(){
    let me = 'inside'
    function b(){
        console.log(me)
    }
    b()
}
// inside

b是在a的scope所以會印出chloe2。但換個情況:

var me = 'global'
a()
function a(){
    let me = 'inside'

    b()
}
function b(){
    console.log(me)
}
// global

這會印出global,因為對b來說他沒有me,就會往外部找,他的外部就是全域中的me:global。

那麼This又是怎麼指向的?
我們知道傳統函式的this會指向呼叫的是誰。所以下面這個例子,無意外的this都會指向window。

a()
function a(){
    console.log('a',this)
    function b(){
        console.log('b',this)
    }
    b()
}
//a window
//b window

如果使用內部函數的方式:

var name = 'global'
var obj = {
    name: 'obj name',
    f1: function(){
        console.log('f1: ', this.name);
        var f2 = function(){ console.log('f2: ', this.name); }
        f2();
    }
};

obj.f1();

結果會是:

f1:  obj name
f2:  global

我們順一下思路,obj呼叫了f1,所以obj的this會指向obj,f1會印出obj name。
那麼在obj.f1()中執行的f2呢?f2沒有指定呼叫者,視為簡單呼叫,因此this會指向全域,會往外找到全域變數name: 'global'。順便提一下,如果把var name = 'global'改成let name = 'global',f2()會印出undefined,因為用let的全域變數,沒有儲存在window object上面過。

試試看加上arrow function:
arrow function 的this會指向語彙位子。我們試著將f2改成arrow function

var name = 'global'
var obj = {
    name: 'obj name',
    f1: function(){
        console.log('f1: ', this.name);
        var f2 = ()=>{ console.log('f2: ', this.name); }
        f2();
    }
};

obj.f1();

會得到:

f1: obj name
f2: obj name

為什麼呢?
f1會得到obj name是因為是obj呼叫f1,所以this會指向obj。
f2使用arrow function,this會指向語彙位子的上一層也就是obj。

再來試著將f1改成arrow function,f2維持傳統函式。

var name = 'global'
var obj = {
    name: 'obj name',
    f1: ()=> {
        console.log('f1: ', this.name);
        var f2 = function(){ console.log('f2: ', this.name); }
        f2();
    }
};

obj.f1();

試著想一下會得到什麼?

f1: global
f2: global

你答對了嗎?
f1:雖然是obj呼叫f1,但f1是arrow funciton,他的this會指向語彙位子的上層,也就是window,因此會印出'global'
f2:沒有呼叫者,算簡單呼叫,因此會指向window,也會印出'global'

最後我們再來試試看,兩者都是arrow function的話會是什麼情況呢?

var name = 'global'
var obj = {
    name: 'obj name',
    f1: ()=> {
        console.log('f1: ', this.name);
        var f2 = ()=>{ console.log('f2: ', this.name); }
        f2();
    }
};

obj.f1();
f1: global
f2: global

f1:雖然是obj呼叫f1,但f1是arrow funciton,他的this會指向語彙位子的上層,也就是window,因此會印出'global'
f2:是arrow funciton,他的this會指向語彙位子的上層,他的語彙位子上層obj.f1的this指向window,因此f2的this指向window,所以也會印出'global'。

雖然和上一種寫法得到的結果一樣,但過程是不一樣的喔!










Related Posts

Laravel 5 component & slot

Laravel 5 component & slot

單元測試 (Unit Test)

單元測試 (Unit Test)

[工作日誌 2021.11.10] 第一次面試紀錄

[工作日誌 2021.11.10] 第一次面試紀錄


Comments