手动封装es5中 bind apply call

1.call(不传参版本)

// 函数原型上添加 myCall方法 来模拟call
Function.prototype.myCall = function(obj){
  if(obj === null || obj === undefined){
    obj = window;
  }else{
    obj = object(obj);
  }
//我们要让传入的obj成为, 函数调用时的this值. obj._fn_ = this; //在obj上添加_fn_属性,值是this(要调用此方法的那个函数对象)。 val = obj._fn_(...arg); //不能直接return obj._fn_(...arg) 这样就不delete属性了 obj._fn_(); //在obj上调用函数,那函数的this值就是obj. delete obj._fn_; // 再删除obj的_fn_属性,去除影响. //_fn_ 只是个属性名 你可以随意起名,但是要注意可能会覆盖obj上本来就有的属性 return val }

 

如果要传参的话可以借用es6里面的拓展运算符 (...)(省略了其他的判断)

Function.prototype.myCall = function(obj,...arg){
    obj._fn_ = this;
    obj._fn_(...arg);
    delete obj._fn_;
}

测试一下

let test = {
    name:'test'
}
let o = {
    name:'o',
    fn:function(){
        console.log(this.name, ...arguments);  //这里把参数显示一下
    }
}
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

不用es6可以用eval

Function.prototype.myCall = function(obj){
    let arg = [];
   let val;
for(let i = 1 ; i<arguments.length ; i++){ arg.push( 'arguments[' + i + ']' ) ; // 这里要push 这行字符串 而不是直接push 值 // 因为直接push值会导致一些问题 // 例如: push一个数组 [1,2,3] // 在下面? eval调用时,进行字符串拼接,JS为了将数组转换为字符串 , // 会去调用数组的toString()方法,变为 '1,2,3' 就不是一个数组了,相当于是3个参数. // 而push这行字符串,eval方法,运行代码会自动去arguments里获取值 } obj._fn_ = this; val = eval( 'obj._fn_(' + arg + ')' ) // 字符串拼接,JS会调用arg数组的toString()方法,这样就传入了所有参数 delete obj._fn_;
   return val }
//测试 let test = { name:'test' } let o = { name:'o', fn:function(){ console.log(this.name, ...arguments); //这里把参数显示一下 } } 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

2.apply(用上面的mycall版本)

// ES6
Function.prototype.myApply = function(obj,arr){
    let args = [];
    for(let i = 0 ; i<arr.length; i++){
        args.push( arr[i] );
    }
    // 其实直接 ...arr 传参也可以 但是效果就和aplly有微小差别了
    return this.myCall(obj, ...args);
}
// eval
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
}

3.bind(借用apply,call)

 Function.prototype.bind = function(son){
        var that = this;
        var parent = Array.prototype.slice.call(arguments);//具有length属性的对象转成数组
        return function() {
            return that.apply(son, parent.slice(1));
        }
    }

es5

Function.prototype.myBind = function(obj){
    let _this = this;
    let argArr = [];
    let arg1 = [];
    for(let i = 1 ; i<arguments.length ; i++){ // 从1开始 
        arg1.push( arguments[i] ); // 这里用arg1数组收集下参数
        // 获取arguments是从1开始, 但arg1要从 0(i-1)开始
        // 若是用Array.prototype.slice.call(argument)就方便多了
        argArr.push( 'arg1[' + (i - 1)  + ']' ) ; // 如果用arguments在返回的函数里运行 会获取不到这个函数里的参数了
    }
    return function(){
        let val ;
        for(let i = 0 ; i<arguments.length ; i++){ // 从0开始
            argArr.push( 'arguments[' + i + ']' ) ;
        }
        obj._fn_ = _this;
        val = eval( 'obj._fn_(' + argArr + ')' ) ;
        delete obj._fn_;
        return val
    };
}

 

es6

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
    }
}

转载于:https://www.cnblogs.com/wildccy/p/10494001.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值