前端百題斬【014】——js中的這些「this」指向都值得了解

寫該系列文章的初衷是「讓每位前端工程師掌握高頻知識點,爲工做助力」。這是前端百題斬的第14斬,但願朋友們關注公衆號「執鳶者」,用知識武裝本身的頭腦。

img

14.1 簡介

this是javascript中的一個關鍵字,其使用方法相似於一個變量,是執行上下文中一個重要組成部分。其做用是能夠在函數體內部獲取當前的運行環境。

14.2 指向

每一個函數的this是在調用的時候基於函數的執行環境綁定的,this的指向徹底取決於函數的調用位置。(下面均是在瀏覽器環境下進行測試的結果)
  1. 在全局環境下,this 始終指向全局對象(window), 不管是否嚴格模式;
console.log(this); // window
  1. 普通函數內部的this分兩種狀況,嚴格模式和非嚴格模式。

​ (1)非嚴格模式下,this 默認指向全局對象windowjavascript

​ (2)嚴格模式下, this爲undefinedhtml

function fun() {
    console.log(this); // window
}
  1. 對象內部方法的this指向調用這些方法的對象前端

    (1)函數的定義位置不影響其this指向,this指向只和調用函數的對象有關;java

    (2)多層嵌套的對象,內部方法的this指向離被調用函數最近的對象(window也是對象,其內部對象調用方法的this指向內部對象, 而非window)。數組

const obj = {
    a: 10,
    b: 20,
    add: function () {
        return this.a + this.b;
    }
};

console.log(obj.add()); // 30
const add = obj.add;
console.log(add()); // NaN
  1. 原型鏈中的方法的this仍然指向調用它的對象
const obj = {
    a: 10,
    b: 20
};

const prototypeObj = {
    add: function () {
        return this.a + this.b;
    }
};

Object.setPrototypeOf(obj, prototypeObj);

console.log(obj.add()); // 30
  1. 構造函數中的this與被建立的新對象綁定。
function Fun() {
    this.a = 10;
}

const fun = new Fun();
console.log(fun.a); // 10
  1. 當函數經過Function對象的原型中繼承的方法 call() 和 apply() 方法調用時, 其函數內部的this值可綁定到 call() & apply() 方法指定的第一個對象上, 若是第一個參數不是對象,JavaScript內部會嘗試將其轉換成對象而後指向它。(見後續代碼)
  2. 經過bind方法綁定後, 函數將被永遠綁定在其第一個參數對象上, 而不管其在什麼狀況下被調用。(見後續代碼)
  3. 當函數被當作監聽事件處理函數時, 其 this 指向觸發該事件的元素(針對於addEventListener事件)
<button id="testId">按鈕</button>

const btn = document.getElementById('testId');
btn.addEventListener('click', function() {
    console.log(this); // <button id="testId">按鈕</button>
});
  1. 內聯事件中的this指向分兩種狀況:瀏覽器

    (1)當代碼被內聯處理函數調用時,它的this指向監聽器所在的DOM元素前端工程師

    <button onclick="console.log(this)">按鈕</button> // 輸出該DOM節點

(2)當代碼被包括在函數內部執行時,其this指向等同於 函數直接調用的狀況,即在非嚴格模式指向全局對象window, 在嚴格模式指向undefinedapp

<button onclick="clickFun()">按鈕</button>

function clickFun() {
    console.log(this); // window
}
  1. 對於延時函數內部的回調函數的this指向全局對象window(固然能夠經過bind方法改變其內部函數的this指向)
function Fun() {
    this.a = 10;
    this.method = function() {
        setTimeout(function() {
            console.log(this); // window
        }, 1000);
    }
}

const fun = new Fun();
fun.method();
  1. 因爲箭頭函數不綁定this, 它會捕獲其所在(即定義的位置)上下文的this值, 做爲本身的this值,因此 call() / apply() / bind() 方法對於箭頭函數來講只是傳入參數,對它的 this 毫無影響。
function Fun() {
    this.a = 10;
    this.method = function() {
        setTimeout(() => {
            console.log(this); // Fun {a: 10, method: ƒ}
        }, 1000);
    }
}

const fun = new Fun();
fun.method();

14.3 改變this指向

除了隱式綁定this的方式,還可以經過顯示綁定的方式,經過call、apply、bind方式改變this指向,對於這三者的區別後續將有專門的百題斬去闡述,本節主要進行一波簡單使用。
  1. call()
call() 方法使用一個指定的 this 值和單獨給出的一個或多個參數來調用一個函數。
function method(val1, val2) {
    return this.a + this.b + val1 + val2;
}

const obj = {
    a: 1,
    b: 2
};

console.log(method.call(obj, 3, 4)); // 10
  1. apply()
apply() 方法調用一個具備給定 this值的函數,以及以一個數組(或類數組對象)的形式提供的參數。
function method(val1, val2) {
    return this.a + this.b + val1 + val2;
}

const obj = {
    a: 1,
    b: 2
};

console.log(method.apply(obj, [3, 4])); // 10
  1. bind()
bind() 方法建立一個新的函數,在 bind() 被調用時,這個新函數的 this 被指定爲 bind() 的第一個參數,而其他參數將做爲新函數的參數,供調用時使用。
function method(val1, val2) {
    return this.a + this.b + val1 + val2;
}

const obj = {
    a: 1,
    b: 2
};

const bindMethod = method.bind(obj, 3, 4);
console.log(bindMethod); // [Function: bound method]
console.log(bindMethod()); // 10
擴展
  1. call()apply()的區別是call()方法接受的是參數列表,而apply()方法接受的是一個參數數組
  2. bind返回的是一個綁定函數,而call和apply返回的是運行結果;
  3. 屢次 bind() 是無效的,只會綁定到第一次調用的對象上;
  4. call() / apply() / bind() 方法對於箭頭函數來講只是傳入參數,對它的 this 毫無影響。

1.若是以爲這篇文章還不錯,來個分享、點贊吧,讓更多的人也看到函數

2.關注公衆號執鳶者,與號主一塊兒斬殺前端百題。測試

相關文章
相關標籤/搜索