Function.prototype.call(thisArg [, arg1, arg2, ...])
call() 簡述
- call() 方法 呼叫一個函式, 其具有一個指定的 this 值和分別地提供的引數(引數的列表)。
- 當第一個引數為 null、undefined 的時候, 預設 this 上下文指向window。
call() 簡單例項
const name = 'qianyin';
const product = {
name: 'linheng',
};
function log(...args){
console.log(this.name, ...args);
}
log(1, 2, 3); // qianyin 1 2 3
log.call(null, 1, 2, 3); // qianyin 1 2 3
log.call(product, 1, 2, 3); // linheng 1 2 3
複製程式碼
call() 對箭頭函式無效
const name = 'qianyin';
const product = {
name: 'linheng',
};
const log = (...args) => {
console.log(this.name, ...args);
}
log(1, 2, 3); // qianyin 1 2 3
log.call(null, 1, 2, 3); // qianyin 1 2 3
log.call(product, 1, 2, 3); // qianyin 1 2 3
複製程式碼
補充:
-
箭頭函式作為函式的一種形式, 對於this的處理和普通函式有所區別, 其沒有自己的 this 上下文,也就是說通過 bind/call/apply 函式方法設定 this 值時無效的,會被忽略;
-
因為箭頭函式沒有自己的 this 上下文, 所以箭頭函式的 this 上下文等於定義函式處的this上下文,也就是最近的一個 this 上下文;
-
你可以認為箭頭函式的 this 和呼叫者無關,只和其定義時所在的 this 上下文相關;
-
如下程式碼: 在物件 obj 中使用箭頭函式定義 log 函式, 那麼因為箭頭函式沒有自己的 this 上下文, 所以 log 函式的 this 上下文等於定義箭頭函式處的 this 上下文, 等於 物件 obj 所處的 this 上下文(window)
const name = 'linheng';
const obj = {
name: 'qianyin',
log: () => {
console.log(this.name);
}
};
obj.log(); // linheng
複製程式碼
- 那麼如果我一定要在 obj 中定義一個 log 函式並且使得 this 指向物件 obj 呢?
- 方法一: 使用 function 定義函式
const name = 'linheng';
const obj = {
name: 'qianyin',
log: function(){
console.log(this.name);
}
};
obj.log(); // qianyin
複製程式碼
- 方法二: 多此一舉, 在函式 log 中宣告箭頭函式並呼叫, 那麼箭頭函式的 this 上下文等於定義箭頭函式處的 this 上下文, 等於 log 函式的上下文(物件 obj )
const name = 'linheng';
const obj = {
name: 'qianyin',
log: function(){
(() => {
console.log(this.name);
})();
},
};
obj.log(); // qianyin
複製程式碼
Function.prototype.apply(thisArg [, Array])
Apply() 簡述
- apply() 方法 呼叫 一個具有給定 this 值的函式,以及作為一個 陣列(或類似陣列物件) 提供的引數
- call() 方法的作用和 apply() 方法類似,區別就是除第一引數 call() 方法接受的是 引數列表 ,而apply()方法接受的是一個引數 陣列(或類陣列)。
Apply() 簡單例項
const name = 'qianyin';
const product = {
name: 'linheng',
};
function log(...args){
console.log(this.name, ...args);
}
log([1, 2, 3]); // qianyin [1 2 3]
log.apply(null, [1, 2, 3]); // qianyin 1 2 3
log.apply(product, [1, 2, 3]); // linheng 1 2 3
複製程式碼
Apply() 對箭頭函式無效
const name = 'qianyin';
const product = {
name: 'linheng',
};
const log = (...args) => {
console.log(this.name, ...args);
}
log([1, 2, 3]); // qianyin [1 2 3]
log.apply(null, [1, 2, 3]); // qianyin 1 2 3
log.apply(product, [1, 2, 3]); // qianyin 1 2 3
複製程式碼
Function.prototype.bind(thisArg [, arg1, arg2, ...])
bidn() 簡述
- bind() 方法 建立(拷貝)一個新的函式 , 當這個新函式被呼叫時 this 指向 thisArg,其 引數列表前幾項值 為建立時指定的 引數序列。
- thisArg: 繫結函式被呼叫時,該引數會作為原函式執行時的 this 指向。當使用 new 操作符呼叫繫結函式時,該引數無效。
bind() 繫結 this 上下文
- bind() 最簡單的用法是建立一個函式,使這個函式不論怎麼呼叫都有同樣的 this 上下文。
- JavaScript 新手經常犯的一個錯誤是將一個方法從物件中拿出來,然後再呼叫,卻又希望方法中的 this 是原來的物件(比如在回撥中傳入這個方法)。
- 如果不做特殊處理的話,一般會丟失原來的物件。從原來的函式和原來的物件建立一個繫結函式,則能很漂亮地解決這個問題:
- 如果只是單純繫結 this 上下文, 完全可以使用箭頭函式進行替代
// 例一
this.x = 9;
var module = {
x: 81,
getX: function() { return this.x; }
};
module.getX(); // 返回 81 (通過物件呼叫函式, 上下文為該物件)
var retrieveX = module.getX; // 獲取物件中函式的引用地址
retrieveX(); // 返回 9, 在這種情況下, "this" 指向全域性作用域(在全域性物件下呼叫函式)
// 永久為函式 boundGetX 繫結 this 上下文
var boundGetX = retrieveX.bind(module);
boundGetX(); // 返回 81 (函式 this 上下文永久繫結為 module)
複製程式碼
// 例二為回撥函式繫結 this 上下文
var x = 10;
var obj = {
x: 20,
get: ffunction(){
console.log(this.x);
}
};
// 將物件中方法取出(函式的引用地址),作為回撥函式, 又因為 setTimeout 回撥函式執行的上下文是 window
setTimeout(obj.get, 1000); // 列印 10
// 將物件中方法取出(函式的引用地址),作為回撥函式並繫結 this 上下文
setTimeout(obj.get.bind(obj), 1000); // 列印 20
複製程式碼
為函式永久繫結固定引數
- bind() 的另一個最簡單的用法是使一個函式 擁有預設的初始引數 。
- 這些引數(如果有的話)作為bind()的第二個引數跟在 this(或其他物件)後面。
- 之後它們會 被插入到目標函式的引數列表的開始位置 ,傳遞給繫結函式的引數會跟在它們的後面。
function list() {
return Array.prototype.slice.call(arguments);
}
var list1 = list(1, 2, 3); // [1, 2, 3]
// 為拷貝 list 方法並繫結初始引數
var leadingThirtysevenList = list.bind(undefined, 37);
var list2 = leadingThirtysevenList(); // [37]
var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
複製程式碼
總結
- 當我們使用一個函式需要改變 this 指向的時候才會用到 call() apply() bind() 當然也別忘記了箭頭函式
- call() 和 apply() 是對函式的呼叫,在呼叫的同時繫結 this 上下文並傳遞引數列表
- bind() 是對函式的拷貝進一步的封裝, 為函式永久繫結 this 上下文並賦予固定引數
- call() 和 bind() 以引數列表形式給函式指定引數, apply() 則以陣列的形式給函式指定引數
apply call bind 的一些運用
類陣列轉為陣列
- 方法一:
const obj = {0: 'q', 1: 'i', 2: 'q', 3: 'a', 4:'n', 5: 'y', 6:'i', 7:'n', length: 8};
const arr = [];
Array.prototype.push.apply(arr, obj);
console.log(arr); // ["q", "i", "q", "a", "n", "y", "i", "n"]
複製程式碼
- 方法二:
const obj = {0: 'q', 1: 'i', length: 2};
const arr = Array.prototype.slice.call(obj); // [q, i]
複製程式碼
為偽陣列新增新的元素
- 方法一: 當然你也可以使用 apply
const obj = {0: 'q', length: 1};
Array.prototype.push.call(obj, 'i', 'a', 'n');
console.log(obj); // {0: 'q', 1: 'i', 2: 'a', 3: 'n'}
複製程式碼
- 方法二:
const obj = {0: 'q', length: 1};
const push = Array.prototype.push.bind(obj);
push('i', 'a', 'n');
console.log(obj); // {0: 'q', 1: 'i', 2: 'a', 3: 'n'}
複製程式碼
求陣列中最大值(最小值一樣做法)
const arr = [1,2,3,4,5,6];
const max = Math.max.apply(null, arr);
// 或 const max = Math.max.call(null, ...arr)
console.log(max); // 6
複製程式碼
陣列合並追加
const arr = [1, 2];
const brr = [3, 4];
Array.prototype.push.apply(arr, brr);
// 或者 Array.prototype.push.call(arr, ...brr);
// 當然還可以這樣 arr.push(...brr);
console.log(arr);
複製程式碼
使用 log 代理 console.log
function log(...args){
// 在可以通過配置, 或者判斷當前開發環境來控制是否需要在控制檯列印輸出
if(true){
console.log.apply(console, args);
}
}
複製程式碼