清晰透徹的瞭解JavaScript的this指向問題

this指向

this是函數體內的一個關鍵字, 不同函數和不同的環境都會影響this的值 
function函數, this默認指向函數調用者 
箭頭函數, this指向外層作用域this的值 
node環境中, 注意全局不再是window, 而是global對象 

注意: 嚴格模式下, this無法指向window, 並且 this變量無法重新賦值

this綁定規則

1. 默認綁定: 看調用者

  • 案例1: 按鈕事件中this的值
<button οnclick="btn2()">再點下</button>
function btn2(){ // 標籤上綁定的事件也是在window環境
    console.log(this); // 默認由window調用
}
  • 案例2: 默認window調用方法
function fnOne(){
    console.log(this); // window
}
fnOne(); // 其實是window.fnOne(), 所以調用者是window
  • 案例3: 定時器回調函數
setTimeout(function(){ // setTimeout運行在Window的環境下
    console.log(this);  // 默認指向window
}, 0);
  • 案例4: 匿名自調用函數
(function(){ // 匿名自調用函數運行在window環境下
    console.log(this); // 代碼默認前面都有個window.調用, 所以this默認指向window
})();

2. 隱式綁定: 看調用者

  • 案例1: JS獲取標籤.事件函數
<button id="btn">點我</button>
document.getElementById("btn").onclick = function(){
    console.log(this); // onclick事件綁定到按鈕, 所以this指向btn按鈕
}
  • 案例2: 對象調用函數
let obj = {
    a: "web前端",
    b: function(){
        console.log(this); // obj對象
    }
}
obj.b(); // b函數的調用者是obj
  • 案例3: 取出函數, 再調用
let obj2 = {
    a: "web前端",
    b: function(){
        console.log(this); // window
    }
}
let fn = obj2.b; // 這裏不是調用函數, 而是取出函數
fn(); // 默認前面有個window在調用.
  • 案例4: 返回函數再調用
let obj3 = {
    a: "web前端3",
    b: function(){
        return function(){
            console.log(this); // window
        }
    }
}
let fn2 = obj3.b(); // 調用后里面return出來function(){}賦予給fn2變量
fn2(); // 默認前面有個window
  • 案例5: 返回對象, 調用裏面函數
let obj4 = {
    a: "web前端",
    b: function(){
        return {
            c: "h5啊",
            d: function(){
                console.log(this); // 指向當前c, d爲key的此對象
            }
        }
    }
}
obj4.b().d(); // obj4.b() 得到對象, 然後對象.d()調用的, 所以看好調用者是誰

3. 硬綁定: 直接修改this指向

使用call和apply, 會觸發函數, 替換函數內this該指向的值 
如果不傳或者傳null, 還是默認該指向誰就指向誰 

function Person(){
    this.username = "";
    this.age = 0;
}
let obj = {};
Person.call(obj); // call/apply效果相同, 調用Person函數, 修改函數裏this指向的值爲obj對象, 並且在obj對象上添加username和age屬性
// obj的值: {username: "", age: 0}

4. new 綁定

默認配合構造函數使用, 使this指向新的對象

function objectFactory(Base, ...arr) {
    // Base 是new 後面的類名
    let obj = new Object(); //從Object.prototype上克隆一個對象
    obj.__proto__ = Base.prototype; // 繼承Base原型裏屬性/方法
    Base.call(obj, ...arr); // 繼承Base構造函數裏屬性/方法
    return obj; //返回 obj
}

注意

綁定優先級 new > 硬綁定 > 隱式 > 默認

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章