js-淺析js指向-this繫結優先順序及特殊情況說明
通過之前的分析,我們可以知道常規this有哪些繫結規則,那他們如果同時出現在一個位置進行呼叫,優先順序是怎麼樣的?通過實踐總結,可以得出以下幾條通用規則:
1. 函式是否在new中呼叫(new繫結)?如果是的話,this繫結的是新建立的物件;
2. 函式是否通過call,apply(顯示繫結)或者硬繫結呼叫?如果是的話,this繫結的是指定的物件;
3. 函式是否再某個上下文物件中引用(隱式繫結)?如果是,this繫結的是那個上下文物件;
4. 如果都不是的話,使用預設繫結。如果是在嚴格模式下,就繫結在undefined。否則就繫結在全域性變數上。
在某些場景下,this的繫結不符合上文提到的規律。總結如下:
1. 呼叫call或者apply時,不希望修改this指向,而是用於其他用途,例如apply用於陣列張開。此時第一個引數填入null或者繫結給一個空的非委託物件(使用Object.create()建立的物件)。此時的this會指向全域性物件(在瀏覽器下,這個物件是window)或者空的委託物件。不推薦繫結到全域性物件,會有風險;
2. 賦值的時候極易引發隱式繫結,所以要特別注意;
3. 在ES6中有一個特殊函式:箭頭函式。箭頭函式不是通過function關鍵字定義,而是使用操作符“=>”定義。它不使用this的四種標準規則,而是根據外層(函式或全域性)作用域來決定this。
為了可以更加靈活的繫結this的指向,而不是使用硬繫結強制將this繫結在一個特定的物件上,導致後期無法再次修改。可以使用一種被稱為軟繫結的方法:
if(!Function.prototype.softBind){
var fn = this;
var curried = [].slice.call(arguments,1);
var bound = function(){
return fn.apply(!this|| this === (window || golbal)?obj:this,
curried.concat.apply(curried,arguments ));
};
bound.prototype = Object.create(fn.prototype);
return bound;
}
}
與ES5內建的bind(…)類似。它會對指定的函式進行封裝,首先檢查呼叫時的this,如果this繫結在全域性物件或者undefined,那就會把指定的預設物件obj繫結到this上,否則不會修改this。比起硬繫結,很大程度上提高了程式的靈活性,也可以多次根據實際情況修改this指向。