call函數(shù)實(shí)現(xiàn)
首先先知道call函數(shù)實(shí)現(xiàn)了什么功能
- 函數(shù)在調(diào)用call方法時(shí)會(huì)執(zhí)行當(dāng)前函數(shù)
- call方法的第一個(gè)參數(shù)可以顯示綁定this
- call方法的剩余參數(shù)可以作為調(diào)用函數(shù)的參數(shù)傳入到調(diào)用函數(shù)的執(zhí)行體中進(jìn)行執(zhí)行赁还,并且返回結(jié)果
1.在實(shí)現(xiàn)自定義的call函數(shù)之前妖泄,考慮到自定義call是需要被所有函數(shù)調(diào)用的,那么這里的自定義call必須放到函數(shù)的原型鏈上
Function.prototype.yqcall = function () {
};
function foo() {
}
foo.yqcall()
2.現(xiàn)在來實(shí)現(xiàn)自定義call方法的第一個(gè)功能
Function.prototype.yqcall = function () {
//1.獲取需要執(zhí)行的函數(shù)
var fn = this; //隱式綁定,利用this拿到當(dāng)前調(diào)用yqcall的函數(shù)
fn() //執(zhí)行調(diào)用的函數(shù)
};
function foo() {
console.log(1111);
}
foo.yqcall()
如果直接這樣執(zhí)行fn,是可以看到控制臺(tái)打印結(jié)果的艘策,但是在考慮第二個(gè)功能的情況下蹈胡,需要將傳入的第一個(gè)參數(shù)(thisArgs)作為綁定的this,直接執(zhí)行是不合適的
2.實(shí)現(xiàn)自定義call第二個(gè)功能
Function.prototype.yqcall = function (thisArgs) {
//1.獲取需要執(zhí)行的函數(shù)
var fn = this; //隱式綁定,利用this拿到當(dāng)前調(diào)用yqcall的函數(shù)
//2.執(zhí)行當(dāng)前調(diào)用yqcall的函數(shù)
//fn();
thisArgs.fn = fn; //將fn函數(shù)放到thisArgs對(duì)象中
thisArgs.fn(); //通過隱式綁定將this綁定到y(tǒng)qcall的第一個(gè)參數(shù)上
delete thisArgs.fn; //執(zhí)行完成之后將fn函數(shù)進(jìn)行刪除
};
function foo() {
console.log(1111,this);
}
foo.yqcall({name:"jack"})
輸出結(jié)果
從輸出的結(jié)果看對(duì)象類型中不僅僅有傳入的對(duì)象鍵值對(duì)還有一個(gè)fn函數(shù)朋蔫,這里主要是因?yàn)閖s的運(yùn)行機(jī)制導(dǎo)致的罚渐,如果用c語(yǔ)言來實(shí)現(xiàn)就不會(huì)有這種情況了,如果你在這一步上有更好的想法驯妄,歡迎下面留言
但是這樣還是不太完善荷并,在我這里傳入的參數(shù)是一個(gè)對(duì)象類型,這里就需要考慮傳入的是其他的數(shù)據(jù)類型該這么轉(zhuǎn)化為對(duì)象類型
thisArgs = Object(thisArgs);
在執(zhí)行第二步的操作前可以用Object將參數(shù)轉(zhuǎn)化為對(duì)象類型,這里考慮一些邊界狀況青扔,如果傳入的null和undefined源织,在call函數(shù)中是直接指向的是window,所以這里需要進(jìn)行進(jìn)一步的處理
thisArgs = thisArgs ? Object(thisArgs):window
但是如果傳入的是0微猖,這里的指向會(huì)有一點(diǎn)問題
foo.yqcall('adc');
foo.yqcall(123);
foo.yqcall(null);
foo.yqcall(undefined);
foo.yqcall(0);
結(jié)果
再來做一步處理
thisArgs =(thisArgs !== null && thisArgs !== undefined) ? Object(thisArgs) : window;
這里就不在考慮其他邊界問題了谈息,接下來實(shí)現(xiàn)第三個(gè)功能
3.實(shí)現(xiàn)自定義call的第三個(gè)功能
第三個(gè)功主要是將傳入的剩余參數(shù)做一個(gè)轉(zhuǎn)換然后返回出去,第三個(gè)功能用到了es6的展開運(yùn)算符,將剩余參數(shù)傳入凛剥,然后將參數(shù)展開傳入到執(zhí)行的fn函數(shù)上侠仇,并且將結(jié)果進(jìn)行返回
//實(shí)現(xiàn)call方法
//1.每一個(gè)函數(shù)都需要使用call方法,這里需要將call方法放到函數(shù)的原型鏈上
//2.調(diào)用call方法后需要讓函數(shù)執(zhí)行
Function.prototype.yqcall = function (thisArgs, ...args) { //...args剩余參數(shù)傳入
//1.獲取需要執(zhí)行的函數(shù)
var fn = this; //隱式綁定,利用this拿到當(dāng)前調(diào)用yqcall的函數(shù)
//2.轉(zhuǎn)化第一個(gè)參數(shù)
//由于無(wú)法確定傳入的參數(shù)是否是對(duì)象類型的,這里需要統(tǒng)一將thisArgs轉(zhuǎn)換為對(duì)象類型
//還需要考慮到如果傳入的參數(shù)是null和undefined或者是0的情況下
// thisArgs = thisArgs ? Object(thisArgs):window
thisArgs =
thisArgs !== null && thisArgs !== undefined ? Object(thisArgs) : window;
//3.執(zhí)行當(dāng)前調(diào)用yqcall的函數(shù)
//fn();
thisArgs.fn = fn; //將fn函數(shù)放到thisArgs對(duì)象中
var result = thisArgs.fn(...args); //通過隱式綁定將this綁定到y(tǒng)qcall的第一個(gè)參數(shù)上,并且將參數(shù)展開傳入到函數(shù)中
delete thisArgs.fn; //執(zhí)行完成之后將fn函數(shù)進(jìn)行刪除
//4.返回結(jié)果
return result;
};
function foo(num1, num2) {
console.log(1111,this);
return num1 + num2;
}
var res = foo.yqcall(0, 20, 30);
console.log(res);
最終結(jié)果
apply實(shí)現(xiàn)
apply的功能和call的功能是非常相似的当悔,只有傳入的第二個(gè)參數(shù)參數(shù)有區(qū)別傅瞻,call的傳入的剩余參數(shù),apply傳入的是數(shù)組
Function.prototype.yqapply = function (thisArgs, arrArgs) {
//1.獲取到調(diào)用的函數(shù)
var fn = this;
//2.處理thisArgs的邊界判斷
thisArgs =
thisArgs !== null && thisArgs !== undefined ? Object(thisArgs) : window;
//3.執(zhí)行當(dāng)前調(diào)用的函數(shù)
thisArgs.fn = fn;
arrArgs = arrArgs || []; //如果第二個(gè)參數(shù)沒有傳入數(shù)組,給一個(gè)空的數(shù)組
var result = thisArgs.fn(...arrArgs);
delete thisArgs.fn;
//4.返回結(jié)果
return result;
};
function foo(num1, num2) {
console.log(111, this, num1, num2);
return num1 + num2;
}
var res = foo.yqapply('adc', [20, 30]);
console.log(res);
結(jié)果
bind的實(shí)現(xiàn)
bind和其他兩個(gè)最大的不同時(shí)函數(shù)調(diào)用完之后返回一個(gè)函數(shù)盲憎,返回的函數(shù)在執(zhí)行時(shí)可以傳入新的參數(shù)
function foo(num1, num2, num3, num4) {
console.log(num1, num2, num3, num4, this);
return num1 + num2 + num3 + num4;
}
var bar = foo.bind('adc', 20, 30);
var result = bar(40, 50);
console.log(result);
所以實(shí)現(xiàn)bind方法在上面的基礎(chǔ)上需要實(shí)現(xiàn)的是返回一個(gè)函數(shù)嗅骄,返回的函數(shù)的參數(shù)要加在一起進(jìn)行合并
Function.prototype.yqbind = function (thisArgs, ...arrArry) {
//1.獲取到調(diào)用的函數(shù)
var fn = this;
//2.thisArgs邊界判斷
thisArgs =
thisArgs !== null && thisArgs !== undefined ? Object(thisArgs) : window;
function ProxyFn(...args) { //使用函數(shù)代理
thisArgs.fn = fn;
var FinallyArgs = [...arrArry, ...args]; //將兩次的傳遞的參數(shù)進(jìn)行合并
var result = thisArgs.fn(...FinallyArgs);
delete thisArgs.fn;
return result;
}
return ProxyFn; //將代理函數(shù)返回
};
執(zhí)行的結(jié)果是一樣的
還有其他的邊界判斷實(shí)現(xiàn)可以在下面留言