先分析下3個(gè)方法的作用
- 改變this的指向殴胧。
- 傳入?yún)?shù)牙躺。
- call apply返回函數(shù)結(jié)果, bind 返回新函數(shù)
我們先從 call 開(kāi)始
改變this的指向
首先我們知道扬霜,對(duì)象上的方法,在調(diào)用時(shí)啥寇,this是指向?qū)ο蟮摹?/p>
let o = {
fn:function(){
console.log(this);
}
}
o.fn() // Object {fn: function}
知道了這點(diǎn),我們就可以實(shí)現(xiàn)改變this指向了
// 函數(shù)原型上添加 myCall方法 來(lái)模擬call
Function.prototype.myCall = function(obj){
//我們要讓傳入的obj成為, 函數(shù)調(diào)用時(shí)的this值.
obj._fn_ = this; //在obj上添加_fn_屬性,值是this(要調(diào)用此方法的那個(gè)函數(shù)對(duì)象)辑甜。
obj._fn_(); //在obj上調(diào)用函數(shù),那函數(shù)的this值就是obj.
delete obj._fn_; // 再刪除obj的_fn_屬性,去除影響.
//_fn_ 只是個(gè)屬性名 你可以隨意起名衰絮,但是要注意可能會(huì)覆蓋obj上本來(lái)就有的屬性
}
下面測(cè)試一下
let test = {
name:'test'
}
let o = {
name:'o',
fn:function(){
console.log(this.name);
}
}
o.fn() // "o"
o.fn.call(test) // "test"
o.fn.myCall(test) // "test"
現(xiàn)在,改變this的值磷醋,實(shí)現(xiàn)了
傳入?yún)?shù)
- 最簡(jiǎn)單實(shí)現(xiàn)猫牡,用ES6
// 只需要在原來(lái)的基礎(chǔ)上 用下拓展運(yùn)算符 剩余運(yùn)算符即可
Function.prototype.myCall = function(obj,...arg){
obj._fn_ = this;
obj._fn_(...arg);
delete obj._fn_;
}
//測(cè)試
let test = {
name:'test'
}
let o = {
name:'o',
fn:function(){
console.log(this.name, ...arguments); //這里把參數(shù)顯示一下
}
}
o.fn(1,2,3) // "o" 1 2 3
o.fn.call(test,1,2,3) // "test" 1 2 3
o.fn.myCall(test,1,2,3) // "test" 1 2 3
// 沒(méi)問(wèn)題
不用ES6就比較麻煩了
- 用eval 方法
eval方法,會(huì)對(duì)傳入的字符串邓线,當(dāng)做JS代碼進(jìn)行解析淌友,執(zhí)行。
Function.prototype.myCall = function(obj){
let arg = [];
for(let i = 1 ; i<arguments.length ; i++){
arg.push( 'arguments[' + i + ']' ) ;
// 這里要push 這行字符串 而不是直接push 值
// 因?yàn)橹苯觩ush值會(huì)導(dǎo)致一些問(wèn)題
// 例如: push一個(gè)數(shù)組 [1,2,3]
// 在下面?? eval調(diào)用時(shí),進(jìn)行字符串拼接,JS為了將數(shù)組轉(zhuǎn)換為字符串 骇陈,
// 會(huì)去調(diào)用數(shù)組的toString()方法,變?yōu)?'1,2,3' 就不是一個(gè)數(shù)組了震庭,相當(dāng)于是3個(gè)參數(shù).
// 而push這行字符串,eval方法你雌,運(yùn)行代碼會(huì)自動(dòng)去arguments里獲取值
}
obj._fn_ = this;
eval( 'obj._fn_(' + arg + ')' ) // 字符串拼接器联,JS會(huì)調(diào)用arg數(shù)組的toString()方法,這樣就傳入了所有參數(shù)
delete obj._fn_;
}
//測(cè)試
let test = {
name:'test'
}
let o = {
name:'o',
fn:function(){
console.log(this.name, ...arguments); //這里把參數(shù)顯示一下
}
}
o.fn(1,['a','b'],3) // "o" 1 ["a","b"] 3
o.fn.call(test,1,['a','b'],3) // "test" 1 ["a","b"] 3
o.fn.myCall(test,1,['a','b'],3) // "test" 1 ["a","b"] 3
// 沒(méi)問(wèn)題
返回函數(shù)值
很簡(jiǎn)單,變量保存一下
Function.prototype.myCall = function(obj,...arg){
let val ;
obj._fn_ = this;
val = obj._fn_(...arg); //不能直接return obj._fn_(...arg) 這樣就不delete屬性了
delete obj._fn_;
return val;
}
Function.prototype.myCall = function(obj){
let arg = [];
let val ;
for(let i = 1 ; i<arguments.length ; i++){ // 從1開(kāi)始
arg.push( 'arguments[' + i + ']' ) ;
}
obj._fn_ = this;
val = eval( 'obj._fn_(' + arg + ')' ) // 字符串拼接婿崭,JS會(huì)調(diào)用arg數(shù)組的toString()方法主籍,這樣就傳入了所有參數(shù)
delete obj._fn_;
return val;
}
傳參檢測(cè)
傳入的obj如果是null, undefined 應(yīng)該改為window。如果是string逛球,number千元,boolean應(yīng)該轉(zhuǎn)換為對(duì)象。
- 可以自己加入一下判斷颤绕,為了方便觀看幸海,我就先不加了。
if(obj === null || obj === undefined){
obj = window;
} else {
obj = Object(obj);
}
實(shí)現(xiàn)apply
其實(shí)apply和call差不多奥务,沒(méi)什么大區(qū)別
- 利用已經(jīng)寫好的myCall來(lái)實(shí)現(xiàn)
// ES6
Function.prototype.myApply = function(obj,arr){
let args = [];
for(let i = 0 ; i<arr.length; i++){
args.push( arr[i] );
}
// 其實(shí)直接 ...arr 傳參也可以 但是效果就和aplly有微小差別了
return this.myCall(obj, ...args);
}
// ES3
Function.prototype.myApply = function(obj,arr){
let args = [];
for(let i = 0 ; i<arr.length; i++){
args.push( 'arr[' + i + ']' ); // 這里也是push 字符串
}
return eval( 'this.myCall(obj,' + args + ')' );
}
- 不用myCall
Function.prototype.myApply = function(obj,arr){
let args = [];
let val ;
for(let i = 0 ; i<arr.length ; i++){
args.push( 'arr[' + i + ']' ) ;
}
obj._fn_ = this;
val = eval( 'obj._fn_(' + args + ')' )
delete obj._fn_;
return val
}
- 測(cè)試
Array.apply({},{length:3});
// 返回 [undefined, undefined, undefined]
Array.myApply({},{length:3});
// 返回 [undefined, undefined, undefined]
效果沒(méi)區(qū)別
實(shí)現(xiàn)bind
- ES6 + 寫好的myApple
Function.prototype.myBind = function(obj,...arg1){ //arg1收集剩余參數(shù)
return (...arg2) => { //返回箭頭函數(shù), this綁定調(diào)用這個(gè)方法(myFind)的函數(shù)對(duì)象
return this.myApply( obj, arg1.concat(arg2) ); // 將參數(shù)合并
}
}
- ES6
// 其實(shí)沒(méi)什么說(shuō)的
Function.prototype.myBind = function(obj,...arg1){
return (...arg2) => {
let args = arg1.concat(arg2);
let val ;
obj._fn_ = this;
val = obj._fn_( ...args );
delete obj._fn_;
return val
}
}
- 不用ES6 , 不用myApple
Function.prototype.myBind = function(obj){
let _this = this;
let argArr = [];
let arg1 = [];
for(let i = 1 ; i<arguments.length ; i++){ // 從1開(kāi)始
arg1.push( arguments[i] ); // 這里用arg1數(shù)組收集下參數(shù)
// 獲取arguments是從1開(kāi)始, 但arg1要從 0(i-1)開(kāi)始
// 若是用Array.prototype.slice.call(argument)就方便多了
argArr.push( 'arg1[' + (i - 1) + ']' ) ; // 如果用arguments在返回的函數(shù)里運(yùn)行 會(huì)獲取不到這個(gè)函數(shù)里的參數(shù)了
}
return function(){
let val ;
for(let i = 0 ; i<arguments.length ; i++){ // 從0開(kāi)始
argArr.push( 'arguments[' + i + ']' ) ;
}
obj._fn_ = _this;
val = eval( 'obj._fn_(' + argArr + ')' ) ;
delete obj._fn_;
return val
};
}
測(cè)試下
let test = {
name:'test'
}
let o = {
name:'o',
fn:function(){
console.log(this.name, ...arguments); //這里把參數(shù)顯示一下
}
}
//myBind
b = o.fn.myBind(test,1,2)
b() // "test" 1 2
b(3,4) // "test" 1 2 3 4
// bind
b = o.fn.bind(test,1,2)
b() // "test" 1 2
b(3,4) // "test" 1 2 3 4
三個(gè)方法的我寫的代碼
- 模擬call
Function.prototype.myCall = function(obj){
if(obj === null || obj === undefined){
obj = window;
} else {
obj = Object(obj);
}
let arg = [];
let val ;
for(let i = 1 ; i<arguments.length ; i++){
arg.push( 'arguments[' + i + ']' ) ;
}
obj._fn_ = this;
val = eval( 'obj._fn_(' + arg + ')' )
delete obj._fn_;
return val
}
- 模擬apply
Function.prototype.myApply = function(obj,arr){
if(obj === null || obj === undefined){
obj = window;
} else {
obj = Object(obj);
}
let args = [];
let val ;
for(let i = 0 ; i<arr.length ; i++){
args.push( 'arr[' + i + ']' ) ;
}
obj._fn_ = this;
val = eval( 'obj._fn_(' + args + ')' )
delete obj._fn_;
return val
}
- 模擬bind
Function.prototype.myFind = function(obj){
if(obj === null || obj === undefined){
obj = window;
} else {
obj = Object(obj);
}
let _this = this;
let argArr = [];
let arg1 = [];
for(let i = 1 ; i<arguments.length ; i++){
arg1.push( arguments[i] );
argArr.push( 'arg1[' + (i - 1) + ']' ) ;
}
return function(){
let val ;
for(let i = 0 ; i<arguments.length ; i++){
argArr.push( 'arguments[' + i + ']' ) ;
}
obj._fn_ = _this;
console.log(argArr);
val = eval( 'obj._fn_(' + argArr + ')' ) ;
delete obj._fn_;
return val
};
}
謝謝閱讀物独,有任何問(wèn)題請(qǐng)指出。歡迎一起討論氯葬。