一文搞定this問題
阿新 • • 發佈:2021-01-20
this 可能屬於 JavaScript 開發中老生常談的問題了,但 this 相關問題依舊困擾著許多開發同學,這裡將之前總結的一篇筆記與大家分享。
this 預設繫結
預設繫結時,this 指向全域性即,window。值得注意的是,在嚴格模式環境中,預設繫結的this指向undefined。
function fn() { console.log(this); //window console.log(this.name); }; function fn1() { "use strict"; console.log(this); //undefined console.log(this.name); }; var name = 'Jack'; fn(); // window // Jack fn1() // undefined // Uncaught TypeError: Cannot read property 'name' of undefined
this 隱式繫結
- 如果函式作為一個物件的屬性呼叫,那麼它的 this 指向這個物件。
// 例子1 function fn() { console.log(this.name); }; let obj = { name: 'LiLei', func: fn }; obj.func() // LiLei // 例子2 // 如果函式呼叫前存在多個物件,this 指向距離呼叫自己最近的物件 function fn() { console.log(this.name); }; let obj = { name: 'Jack', func: fn, }; let obj1 = { name: 'Tom', o: obj }; obj1.o.func() // Jack
- 隱式丟失
// 例子1. 最常見的就是作為引數傳遞以及變數賦值時,會存在隱式繫結丟失的問題。 var name = 'LiLei'; let obj = { name: 'Tom', fn: function () { console.log(this.name); } }; function fn1(param) { param(); }; fn1(obj.fn);// LiLei // 例子2. 變數賦值 var name = 'LiLei'; let obj = { name: 'Tom', fn: function () { console.log(this.name); } }; let fn1 = obj.fn; fn1(); // LiLei
this 顯式繫結
通過call、apply以及bind方法改變this的行為我們叫做顯式繫結繫結,相比隱式繫結 this 的指向更加明顯。
let obj1 = {
name: 'A'
};
let obj2 = {
name: 'B'
};
let obj3 = {
name: 'C'
}
var name = 'D';
function fn() {
console.log(this.name);
};
fn(); // D
fn.call(obj1); // A
fn.apply(obj2); // B
fn.bind(obj3)(); // C
如果以上方法第一個引數傳遞的是 null 或者 undefined,this 將指向 window。
bind、call、apply 都可以改變 this 指向,但他們的區別是什麼:
- call、apply在改變this指向的同時還會執行函式,而bind在改變this後是返回一個全新的boundFcuntion繫結函式,這也是為什麼上方例子中bind後還加了一對括號 ()的原因。
- bind屬於硬繫結,返回的 boundFunction 的 this 指向無法再次通過bind、apply或 call 修改;call與apply的繫結只適用當前呼叫,呼叫完就沒了,下次要用還得再次綁。
- call與apply功能完全相同,唯一不同的是call方法傳遞函式呼叫形參是以雜湊形式,而apply方法的形參是一個數組。在傳參的情況下,call的效能要高於apply,因為apply在執行時還要多一步解析陣列。
new 方法
new 一個函式主要發生了一下幾步:
- 以構造器的 prototype 屬性為原型,建立新物件;
- 將this(可以理解為上句建立的新物件)和呼叫引數傳給構造器,執行;
- 如果構造器沒有手動返回物件,則返回第一步建立的物件
this繫結優先順序
如果一個函式呼叫存在多種繫結方法,this最終指向誰呢?可以記住以下優先順序:
- 顯式繫結 > 隱式繫結 > 預設繫結
- new繫結 > 隱式繫結 > 預設繫結
// 顯式 > 隱式
let obj = {
name:'A',
fn:function () {
console.log(this.name);
}
};
obj1 = {
name:'B'
};
obj.fn.call(obj1);// B
// new>隱式
obj = {
name: 'C',
fn: function () {
this.name = 'D';
}
};
let echo = new obj.fn();
echo.name;// D
箭頭函式的 this
ES6的箭頭函式中其實並沒有 this,它裡面的 this 指向取決於外層作用域中的 this,外層作用域或函式的this指向誰,箭頭函式中的this便指向誰。
function fn() {
return () => {
console.log(this.name);
};
};
let obj1 = {
name: 'A'
};
let obj2 = {
name: 'B'
};
fn.call(obj1)(); // fn this指向obj1,箭頭函式this也指向obj1
fn.call(obj2)(); // fn this 指向obj2,箭頭函式this也指向obj2