一、概念
改變函數(shù)體內(nèi)部的 this 指向
二翔悠、call
call 第一個參數(shù)定義 this的指向业崖, 后面?zhèn)魅氲氖且粋€參數(shù)列表。
當(dāng)?shù)谝粋€參數(shù)為null蓄愁、undefined的時候双炕,默認(rèn)指向window。 --->(接收單獨(dú)變量)
window.color = 'red';
var o = {color: 'blue'};
function sayColor() {
console.log(this.color);
};
sayColor(); // red
sayColor.call(this); // red
sayColor.call(window); // red
sayColor.call(o); // blue
sayColor.call(); // red
sayColor.call(null); // red
sayColor.call(undefined); // red
實現(xiàn)原理
var foo = {count: 1};
function bar() {
console.log(this.count);
};
bar.myCall(foo); // 1
Function.prototype.myCall = function(context) {
// 獲取傳入的上下文
let context = context || window;
// 給context添加一個屬性涝登,這里的this指向調(diào)用myCall的函數(shù)雄家,比如bar函數(shù)
context.fn = this;
// 通過展開運(yùn)算符和結(jié)構(gòu)賦值取出 context 后面的參數(shù),上文的例子沒有傳入?yún)?shù)列表
let args = [...arguments].slice(1);
// 執(zhí)行函數(shù) (相當(dāng)于上文的 bar(...args))
let result = context.fn(...args);
// 刪除函數(shù)
delete context.fn;
return result
}
應(yīng)用場景
判斷對象類型
var arr1 = [];
console.log(Object.prototype.toString.call(arr1)); // [object Array]
console.log(arr1.toString()); // 空
這是因為 toString 是 Object的原型方法胀滚,Array或者Function等引用類型作為Object的實例趟济,都重寫了toString方法,只會轉(zhuǎn)為字符串類型咽笼。
想要獲得對象的具體類型時顷编,應(yīng)該調(diào)用Object上原型toString方法
apply
apply 接受兩個參數(shù), 第一個參數(shù)是要綁定給this的值剑刑,第二個參數(shù)是一個參數(shù)數(shù)組媳纬。
當(dāng)?shù)谝粋€參數(shù)為null、undefined的時候施掏,默認(rèn)指向window钮惠。 --->(可以接收數(shù)組)
實現(xiàn)原理
var foo = {count: 1};
function bar() {
console.log(this.count);
};
bar.myApply(foo); // 1
Function.prototype.myApply = function(context) {
let context = context || window;
context.fn = this;
let result;
// 判斷第二個參數(shù)是否存在,也就是context后面有沒有一個數(shù)組
// 如果存在七芭,則需要展開數(shù)組
if(arguments[1]) {
result = context.fn(...arguments[1]);
} else {
result = context.fn();
}
delete context.fn;
return result
}
應(yīng)用場景
- 找出數(shù)組中最大或者最小的元素
var arr2 = [10, 2, 5, 28, 9];
Math.max.apply(Math, arr2); // 28
Math.min.apply(null, arr2) // 2
- 將偽數(shù)組轉(zhuǎn)為真正的數(shù)組
Array.prototype.slice.apply({0: 1, length: 1}); // [1]
Array.prototype.slice.apply({0: 1, length: 2}); // [1, undefined]
- 數(shù)組追加
let arr3 = [1, 2, 3];
let arr4 = [4, 5, 6];
[].push.apply(arr1, arr2);
console.log(arr1) // [1, 2, 3, 4, 5, 6]
bind
bind 第一個參數(shù)是 this 的指向素挽, 第二個參數(shù)開始是接收的參數(shù)列表
區(qū)別在于bind方法返回值是函數(shù)以及bind接收的參數(shù)列表的使用。
將某個函數(shù)指針(函數(shù)名)以值的形式進(jìn)行傳遞狸驳,同時該函數(shù)必須在特定環(huán)境中執(zhí)行预明,被綁定函數(shù)的效用就凸顯出來了
// 例子1
var obj = {
name: 'Ming'
};
function printName() {
console.log(this.name);
};
var dot = printName.bind(obj)
console.log(dot); // function
dot() // Ming
printName() // undefined
// 例子2
var a ={
name : "Cherry",
fn : function (a,b) {
console.log( a + b)
}
}
var b = a.fn;
b.call(a,1,2);//立即調(diào)用該函數(shù)
b.bind(a,1,2)();//手動調(diào)用(),它返回一個原函數(shù)的拷貝(新的耙箍,不是原函數(shù))撰糠,并擁有指定的this值和初始參數(shù)。
// 例子3
var objectSayColor = sayColor.bind(o);
objectSayColor(); // blue
sayColor() 調(diào)用 bind() 并傳入對象o, 創(chuàng)建了objectSayColor() 函數(shù)辩昆,objectSayColor() 函數(shù)的this 值等于 o阅酪,
因此即便是在全局作用域中調(diào)用這個函數(shù),也是打印出 ‘blue’
實現(xiàn)原理
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
};
let _this = this;
let args = [...arguments].slice(1);
// 返回一個函數(shù)
return function F() {
if(this instanceof F) {
return new _this(...args, ...arguments)
}
return _this.apply(context, args.concat(...arguments))
}
}
區(qū)別
call 和 apply作用相同,區(qū)別在于傳參方式遮斥,第一個為this指向的上下文環(huán)境峦失,后面有多個參數(shù)的話,call需要按順序一個一個傳遞术吗,apply只需要傳遞一個數(shù)組
bind 是返回對應(yīng)函數(shù)尉辑,方便需要的時候調(diào)用,call 和 apply 是立即調(diào)用
用哪個较屿,取決于你采取哪種給函數(shù)傳遞參數(shù)的方式最方便