func.call(thisArg, param1, param2, ...)//func是个函数
func.apply(thisArg, [param1,param2,...])
func.bind(thisArg, param1, param2, ...)
返回值 :
call / apply : 返回func值行結果。
bind : 返回func的拷貝,並擁有指定的this值合初始參數。
參數 :
thisArg (可選) :
param1、param2(可選):傳給func的參數。
call、apply、bind是掛在Function對象上的三個方法,只有函數有這些方法,例如: Object.prototype.toString就是函數,我們經常看到這樣的用法Object.prototype.tostring.call(data)
執行 :
返回值 :
類數組的特徵有,可以透過索引(index)調用,如array[0];具有長度屬性,可以透過for循環或forEach方法。
那類數組是什麼? 顧名思義就是具備與數組特徵類似的對象。例如下面例子 :
let arrayLike = {
0: 1,
1: 2,
2: 3,
length: 3
};
類數組比較常見在譬如,獲取DOM節點的方法,返回的就是一個類數組,在函數中使用augument所獲取的所有參數,也是類數組。
但注意,類數組無法使用splice,push等數組原型練上的方法,那就要用到call/apply/bind的核心理念---借用方法:
以類數組做比喻,如果它想借用Array原型鍊上slice的方法,可以這樣:
let domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));
// 類數組對象
let arrayLike = {
0: "name",
1: "height",
length: 2
};
Array.prototype.push.call(arrayLike, "weight", "id");
console.log(arrayLike);
// {"0":"name","1":"height","2":"weight","3":"id","length":4}
function hash() {
alert( [].join.call(arguments) ); // 1,2
}
hash(1, 2);
apply直接使用數組作為參數,也省去了展開數組這一步。
const arr = [15, 6, 12, 13, 16];
const max = Math.max.apply(Math, arr); // 16
const min = Math.min.apply(Math, arr); // 6
參數數量不確定的例子 :
const obj = {
age: 24,
name: 'John',
}
const obj2 = {
age: 25
}
// 依據某些條件來決定要傳入的參數數量與順序
function callObj(thisAge, fn) {
let params = [];
if (thisAge.name) {
params.push(thisAge.name);
}
if (thisAge.age) {
params.push(thisAge.age);
}
fn.apply(thisAge, params); // 參數數量不確定,用apply
}
function handle(...params) {
console.log('params', params);
}
callObj(obj, handle); // params ["John", 24]
callObj(obj2, handle); // params [25]
經典面試題 :
for(var i = 1; i < 5; i++) {
setTimeout(function test() {
console.log(i) // 5 5 5 5 5
},i*1000)
}
原因在於等到非同步setTimeout執行時,i已經變成5了。用bind可以解決此問題 :
for(var i = 1; i < 5; i++) {
setTimeout(function test(i) {
console.log(i) // 5 5 5 5 5
}.bind(null, i),i*1000)
}
實際上這裡也運用了閉包。它保存了函數this的指向(setTimeout與setInterval預設都是指向window,這邊只是為了只用bind方法,null也是指向window)、初始參數,每次i邊更都會被bind的閉包存起來,所以輸出1~5。
class Cat {
constructor(name,callback) {
this.name = name;
this.callback = callback;
this.callback();
}
}
class Dog {
constructor(name) {
this.name = name;
this.friend = new Cat('white cat',this.run)
}
run() {
console.log(`${this.name} run with cat!`);
}
}
new Dog("white dog");
我們預期希望是顯示出white dog run with cat!,但實際上顯示出white cat run with cat!,原因在於當new Cat時,所傳入的this.run是記憶體位子(call by reference),並沒有邦定this的指向,這時候可以用bind來解決:
class Cat {
constructor(name,callback) {
this.name = name;
this.callback = callback;
this.callback();
}
}
class Dog {
constructor(name) {
this.name = name;
this.friend = new Cat('white cat',this.run.bind(this))
}
run() {
console.log(`${this.name} run with cat!`);
}
}
new Dog("white dog"); // white dog run with cat!
或是把run改為arrow function也可以:
class Cat {
constructor(name,callback) {
this.name = name;
this.callback = callback;
this.callback();
}
}
class Dog {
constructor(name) {
this.name = name;
this.friend = new Cat('white cat',this.run.bind(this))
}
run = () => {
console.log(`${this.name} run with cat!`);
}
}
new Dog("white dog"); // white dog run with cat!