call、 apply、bind的底层原理

总结:

  1. 三者都可以改变函数的this
  2. 三者第一个参数都是this要指向的对象,如果没有这个参数或参数为undefined或null则默认指向全局window
  3. 三者都可以传参,apply是数组,而call是参数列表,bind也是参数列表但可以分多次传入,apply和call只能一次传完
  4. bind是返回绑定this之后的函数,apply、call则是立即执行

call

用法:func.call(thisArg, param1, param2, …)
call方法执行的时候完成了一些功能:

  • 让当前函数执行
  • 把函数的this指向改为第一个传递给call的实参
  • 把传递给call其余的实参当作参数信息传递给当前函数
  • 如果一个参数都没传,非严格模式下,函数中的this指的是window。严格模式下,指的是undefined

call的实现原理

  Function.prototype.myCall = function (context) {
    // 1. 判断有没有传入要绑定的对象,没有默认为window;如果是基本类型的话通过Object()方法进行转换
    var context = Object(context) || window;

    // 2. 给context添加一个fn属性,值为this,也就是fn()
    context.fn = this;

    // 3. 保存返回值
    let result = "";

    // 4. 取出传递的参数,第一个参数是this
    // 截取除第一个参数外剩余参数的方法
    const args = [...arguments].slice(1);
    // const args = Array.prototype.slice.call(arguments , 1);
    // const args = Array.from(arguments).slice(1);

    // 5. 执行方法,传入参数
    // ... 是es6的展开数组
    result = context.fn(...args);

    // 6. 删除该属性
    delete context.fn;

    // 7. 返回结果
    return result;
  };

  // 测试用例
  const obj = {
    value: "hello",
  };
  window.value = "aaa";
  function fn() {
    return {
      value: this.value,
    };
  }
  let res = fn.myCall(obj);
  console.log(res);

apply

用法:func.apply(thisArg, [param1,param2,…])
apply 和 call 基本原理是差不多的,只是参数存在区别。

实现 apply

Function.prototype.myApply = function( context , args ){
    var context = Object(context) || window;
    context.fn = this;
    let result = '';

    // 4. 判断有没有传入第二个参数 args,如果传入就将第二个参数展开
    if(!args){
        // 没有传入,直接返回该函数
        result = context.fn();
    }else{
        // 传入了,将参数展开
        result = context.fn(...args);
    }

    delete context.fn;
    return result;
}

// 测试用例
const obj = {
    value :'hello'
}
function fn(name , age){
    return {
        value :this.value ,
        name , 
        age
    }
}
let res = fn.myApply(obj ,[ 'LL' , 25]);
console.log(res) // { value: 'hello', name: 'LL', age: 25 }

bind

用法:func.bind(thisArg, param1, param2, …)
bind执行的功能:和apply、call一样,也是改变函数中的this关键字,只不过基于bind改变this,当前方法并没有被执行,类似于预先改变this

Function.prototype.myBind = function( context ){
    // 1. 判断this是不是一个函数
    if(typeof this !== 'function'){
        // 不是函数,抛出错误
        throw new Error('不是一个函数');
    }
    // 2. 暂存this
    const self = this;

    // 3. 获取传入的参数
    // 拿到第一组参数,如果没传,是一个空数组
    const args1 = [...arguments].slice(1);

    // 第二次调用bindFn
    const bindFn = function(){
        // 获取第二个参数
        const args2 = [...arguments];
        // 将第一部分参数和第二部分参数合并到一起,进行返回
        return self.apply(context , args1.concat(args2));
    }
    return bindFn
}

// 测试用例
const obj = {
    value :'hello'
}

function fn(name , age){
    return {
        value :this.value ,
        name , 
        age
    }
}
let res = fn.myBind(obj)('HH' , 30);
console.log(res)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值