apply與call的作用
apply與call的作用是在特定的作用域中呼叫函式
num = 1;// 默認宣告到全域作用域
function logNum() {
console.log(this.num);
}
logNum(); // 1
logNum.apply({ num: 99 });// 99
logNum.call({ num: 88 });// 88
如上代碼可知:call和apply接收的第一個引數都是一個作用域,在作用域里面num都被賦了不一樣的值,所以最后this.num輸出不一樣的值,
這是函式沒有接收引數當情況,當函式有引數時,是這樣的:
sum = 1;
function getSum(num1, num2) {
this.sum = this.sum + num1 + num2;
console.log(this.sum);
}
getSum(2, 3); // 6
getSum.apply({ sum: 100 }, [99, 33]); // 232
getSum.apply({ sum: 100 }, [99, 33, 999]); // 232
// getSum.apply({sum:100},99,33); // TypeError: CreateListFromArrayLike called on non-object
getSum.call({ sum: 100 }, 99, 133); // 232
getSum.call({ sum: 100 }, 99, 133, 99); // 232
getSum.call({ sum: 100 }, [99, 33]); // 10099,33undefined 自動轉為string再拼接了
由以上例子可以看出,apply與call的不同點只是接收的引數不一樣
- apply接收的引數是一個陣列,這個陣列的全部會按照順序傳遞給執行的函式,
- call接收的引數是和執行函式一樣的形式,一個個賦值給執行函式,就是說除了第一個作用域引數,其他引數都會傳遞給執行函式
注意
有一個比較特殊的是apply函式還可以接收arguments物件
sum = 1;
function getSum(num1, num2) {
this.sum = this.sum + num1 + num2;
console.log(this.sum);
}
function myGetSum() {
console.log(arguments); // [Arguments] { '0': 99, '1': 88 }
getSum.apply({ sum: 100 }, arguments);
}
getSum(1, 2);
myGetSum(99, 88);
所以apply的第二個引數只能是陣列或者arguments物件,假設是普通的物件,無法被決議,最終只會傳入undefined到執行函式中,
bind的作用
不接收引數時
num = 1;
function logNum() {
console.log(this.num);
}
const myLogNum = logNum.bind({ num: 100 });
logNum(); // 1
myLogNum(); // 100
接收引數時
sum = 1;
function getSum(num1, num2) {
this.sum = this.sum + num1 + num2;
console.log(this.sum);
}
const myGetSum = getSum.bind({ sum: 100 }, 100, 200);
getSum(100,200); // 301
myGetSum(); // 400
bind的作用是將一個作用域系結到一個函式中,再回傳這個已經系結作用域的函式,
執行該函式時與apply和call是一樣的效果,對!需要執行,不會直接呼叫,
apply,call與bind的相同點
- 都是函式非繼承而來的方法
- 函式有兩個屬性:
- length
- prototype(來繼承屬性與方法)
- 函式有三個非繼承而來的方法:
- call
- apply
- bind
- 函式有兩個屬性:
- 都可以改變函式對運行作用域,也可以說改變this對指向
apply,call與bind的不同點
bind有回傳值:一個系結作用域后的函式實體
apply和call的回傳值是執行函式的回傳值(如果沒有回傳值則回傳:undeifined)
bind回傳一個函式之后需要呼叫才執行
apply和call直接執行函式
apply與call的不同點
接收的引數不一樣:
- apply:接收一個包含多個引數的陣列(oThis,[args1,…])
- call:接收多個引數的串列(oThis,args1,…])
手寫bind(簡單版,了解原理)
bind其實是通過call或者apply實作的,按照接收引數的形式call更適合一些,當然apply也可以使用argument物件或者陣列來實作,但使用call可以節省引數決議的時間,
實作一個簡單的bind函式
Function.prototype.myBind = function (runArea,...args) {
const oThis = this; // 函式實體
return function () {
return oThis.call(runArea,...args); // 加上return 是因為函式可能會有回傳值
};
};
num = 1;
function logNum() {
console.log(this.num);
}
logNum(); // 1
const logByBind = logNum.bind({ num: 100 });
logByBind(); // 100
const logByMyBind = logNum.myBind({ num: 100 });
logByMyBind(); // 100
步驟其實挺簡單的:
- 宣告一個bind函式(myBind)
- 在bind函式里面回傳一個函式
- 在回傳的函式中,執行call方法,加上return可以回傳方法的回傳值
手寫call(簡單版,了解原理)
Function.prototype.myCall = function (runArea, ...args) {
const oThis = runArea || window;
const fn = Symbol('fn'); // 防止替換掉函式實體本身掉fn方法
oThis[fn] = this; // 系結函式實體的作用域
oThis[fn](...args); //執行,看有人用eval來執行,不太推薦
delete oThis[fn]; //執行后洗掉這個方法
};
手寫apply(簡單版,了解原理)
Function.prototype.myApply = function (runArea, args) {
const oThis = runArea || window;
const fn = Symbol("fn"); // 防止替換掉函式實體本身掉fn方法
oThis[fn] = this; // 系結函式實體的作用域
oThis[fn](...args); //執行,看有人用eval來執行,不太推薦
delete oThis[fn]; //執行后洗掉這個方法
};
簡單的實作了下,發現apply和call是可以一樣的,因為引數這里用了解構處理,不管是陣列還是物件都可以處理成串列形式的引數,
總結一下
一遍寫下來發現這三個改變作用域的函式真沒那么復雜,關鍵是多練習多比較,今天就先到這里,未能再深究,要開始寫需求了,今天還用到了Symbol,一直以為它沒啥用處呢,
有錯誤之處希望大家幫忙指出!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/244683.html
標籤:其他
