1. 程式人生 > 實用技巧 >JS 作用鏈,箭頭函式 this 取值

JS 作用鏈,箭頭函式 this 取值

  通俗地講,當宣告一個函式時,區域性作用域一級一級向上包起來,就是作用域鏈。

  1.當執行函式時,總是先從函式內部找尋區域性變數。

  2.如果內部找不到(函式的區域性作用域沒有),則會向建立函式的作用域(宣告函式的作用域)尋找,依次向上。

  比如在閉包中可以直接使用外部函式內定義的變數,這也是閉包的本質:閉包函式的定義基於外部函式的執行。

function outer(){
    let outerSay= 'i am outer';
    function inner(){
        console.log(outerSay);
    }
    return inner;
}

outer()();

  執行結果:

  閉包函式訪問到了外層函式定義的變數。也就是宣告閉包函式時的外層作用域中的變數。

  以另一個例子作對比:

var name = "windowName";

function f1() {
    var name = "f1Name";
    console.log("f1 say : ",name);
    f0();
}


function f0() {
    console.log("f0 say :",name);
}

f1();

  執行結果:

  對於 f1 而言,在其內部聲明瞭 name 變數,所以首先會找到函式內部定義的變數。而對 f0 而言,函式內部沒有定義內部變數,需要從函式定義時的外層環境尋找,也就是全域性變數 name 。而不是 f1 中定義的區域性變數內部, f1 的環境是呼叫 f0 的外部環境,而不是宣告 f0 的外部環境。

  相對於變數的取值取決於函式定義時的環境,this 的取值正相反,取決於函式執行時的上下文。this是使用call方法呼叫函式時傳遞的第一個引數,它可以在函式呼叫時修改,在函式沒有呼叫的時候,this的值是無法確定。對於函式的呼叫:

const obj = {
    name: 'Jerry',
    greet: function() {
        console.log(this.name)
    }
}
obj.greet()  //第一種呼叫方法
obj.greet.call(obj) //第二種呼叫方法

  對於函式的呼叫,第一種方式只是第二種方式的語法糖。函式在呼叫時需要傳入呼叫物件。當使用第一種方法呼叫普通函式時,有以下規則:

  1. 一般方法中,this代指全域性物件 window

  2. 作為物件方法呼叫,this代指當前物件

  3.作為建構函式呼叫,this 指代new 出的物件()

  4. 呼叫方法的apply和call方法,可以改變函式的呼叫物件/作用域

  而對於箭頭函式來說,箭頭函式本身是沒有 this 的,call 傳入的第一個引數會被忽略。在嚴格模式下,this 為 undfined,否則為 window。箭頭函式的 this 取決於執行上下文中的 this 。當作為閉包函式時,執行上下文中有外層函式的 this ,在箭頭函式中使用 this 會直接使用外層函式的 this。閉包函式的定義取決於外部函式的執行。

  其他情況下箭頭函式的 this 均指向 undfined(嚴格模式)或 window。

  對於普通函式來說,做為閉包函式時,其 this 指向的是 window 物件。因為其並沒有被作為物件的方法呼叫,call 方法傳入的第一個方法為 window 。

  可以說閉包函式的 this 是一個特例,其忽略了 call 中傳入的第一個引數,不可人為指定 this,this 只會從執行上下文中去尋找。

  舉個例子:

var name = 'windowName';
var obj = {
    name: 'outerName',
    inner0: function() { console.log('inner0 say : ', this.name); },
    inner1: () => { console.log('inner1 say : ', this.name) }
}
obj.inner0();
obj.inner1();

var name = 'windowName';
var obj = {
    name: 'outerName',
    inner0: function() {
        setTimeout(function() {
            console.log('inner0 say : ', this.name);
        }, 10)

    },
    inner1: function() {
        setTimeout(() => {
            console.log('inner1 say : ', this.name);
        }, 10)
    }
}
obj.inner0();
obj.inner1();