函式內this指向的不同場景,以及如何改變this的指向(bind,call,apply)
一. 對於接觸前端不太久的時候,相信會有個讓大家都感到頭疼的問題,沒錯那就是關於 this 的指向問題。我在剛接觸不久的時候也有過這樣一段時間,不過大家也不用擔心,相信大家只要把這篇部落格認真研究一遍,一定會對 this 會有一些新的認識。
函式的呼叫方式決定了 this 的指向不同:
1. 普通函式呼叫,此時 this 指向 window
function fn() {
console.log(this); // window
}
fn(); // window.fn(),此處預設省略window
2. 建構函式呼叫, 此時 this 指向 例項物件
function Person(age, name) {
this.age = age;
this.name = name
console.log(this) // 此處 this 分別指向 Person 的例項物件 p1 p2
}
var p1 = new Person(18, 'zs')
var p2 = new Person(18, 'ww')
3. 物件方法呼叫, 此時 this 指向 該方法所屬的物件
var obj = {
fn: function () {
console.log(this); // obj
}
}
obj.fn();
4.通過事件繫結的方法, 此時 this 指向 繫結事件的物件
btn.onclick = function() { console.log(this); // btn }
5. 定時器函式, 此時 this 指向 window
setInterval(function () {
console.log(this); // window
}, 1000);
以上五個方面 就是對函式內部 this 指向的基本整理
關於this 的終極總結 : 函式內部的 this 是由呼叫時確定其指向。
二. 接下來一起來看一下如何使用bind,call,apply改變 this的指向,以及他們各自一些小的應用
1.bind()會建立一個新的函式(稱為繫結函式),與被呼叫函式有相同的函式體,當目標函式被呼叫時this的值繫結到 bind()的第一個引數上,如下面demo 此時this
語法: fn.bind(thisArg[, arg1[, arg2[, ...]]])
引數:thisArg 當繫結函式被呼叫時,該引數會作為原函式執行時的 this 指向。當使用new 操作符呼叫繫結函式時,該引數無效。
arg1,arg2.. 當繫結函式被呼叫時,這些引數將置於實參之前傳遞給被繫結的方法
返回值:返回由指定的this值和初始化引數改造的原函式拷貝。
var obj = {name: 'obj'};
obj.fn = function () {
console.log(this)
};
var o = {name: 'o'};
var rel = obj.fn.bind(o);
rel(); // 等價 obj.fn.bind(o)()
2.call() 值和分別地提供的引數(引數的列表)。
語法: fn.call(thisArg[, arg1[, arg2[, ...]]])
引數:thisArg 該引數會作為函式執行時的this指向。(如果指定了 null 或者 undefined 則內部 this 指向 window)
arg1,arg2.. 指定的引數列表
var obj = {name: 'obj'};
obj.fn = function () {
console.log(this)
};
var o = {name: 'o'};
obj.fn.call(o) // 與bind的區別是,call直接呼叫函式
3.apply() this
值,以及作為一個數組(或類似陣列的物件)提供的引數。
語法: fn.apply(thisArg[argArray])
引數:thisArg
argArray
apply() 與call()非常相似,不同之處在於提供引數的方式,apply()使用引數陣列,而不是引數列表
var obj = {name: 'obj'};
obj.fn = function (a, b) {
a = b;
console.log(this)
};
var o = {name: 'o'};
obj.fn.apply(o, [1, 3])
相信已經對 bind,call,apply 有一定的認識了,接下來看幾個案例
案例一 :
需求: 呼叫該物件 fun 方法時 改變其 this指向 打印出 obj 的 name
var obj = {
name : 'zs',
fun: function() {
setInterval(function() {
console.log(this.name);
}.bind(this),1000); // 利用 bind 不呼叫函式特性此處this指向 obj
}
};
obj.fun();
案例二:
var obj = {
0:10,
1:20,
2:30,
length:3
};
Array.prototype.push.call(obj,40);
console.dir(obj);
案例三:
var arr = [4,5,6,8,11,44];
Math.max.apply(Math, arr); // 可以利用apply 第二個引數是陣列。
有什麼不足或者建議歡迎大家評論區探討。謝謝