call()、apply()和bind()手写+分析

一、call()

call()方法被调用的时候到底做了什么,以func.call(obj, param1, param2, param3)为例:

  1. 执行call,并且call的this是点前面的函数func
  2. 立即执行func,并且func的this是传递进去的第一个参数obj,其他的参数…params一个个传递给func
    • 怎么让执行func的时候,this是obj呢?答案是给obj[‘func’] = func,然后obj[‘func’]()就可以了。
    • 但是要注意!!给obj添加属性的时候应该考虑该属性名不能是obj本来就有的。如果obj本来就有func这个属性,那这个属性值就会被覆盖掉。
    • 所以,这里我们考虑用Symbol() 生成一个唯一的值来当属性名
  3. 返回func执行的结果
那就来写代码吧~:
Function.prototype.call = function call(obj, ...params) {
    // 生成唯一的属性名
    let key = Symbol('key');
    //call中的this,就是调用call的func啦
    obj[key] = this;
    //执行func,并且利用展开运算符把参数一个个传递过去
    let result = obj[key](...params);
    // 既然多给对象增加了属性,是不是得删除
    delete obj[key];
    return result;
}


//测试一下
let test = {
    name: "test"
}

const func = function func(behavior) {
    this.behavior = behavior;
    return this;
}

func.call(test, "测试");
console.log(test);//=>{name: "test", behavior: "测试"}
接着来优化一下,处理不传obj和obj为基本类型的情况:
Function.prototype.call = function call(obj, ...params) {
    //obj为null和undefined == null,  这时候让obj=window
    obj == null ? obj = window : null;
    //剩下的基本类型值不能设属性,所以要把它变成对象
    !/^(function|object)$/i.test(typeof obj) ? obj = Object(obj) : null;

    // 生成唯一的属性名
    let key = Symbol('key');
    //call中的this,就是调用call的func啦
    obj[key] = this;
    //执行func,并且利用展开运算符把参数一个个传递过去
    let result = obj[key](...params);
    // 既然多给对象增加了属性,是不是得删除
    delete obj[key];
    return result;
}

二、apply()

call和apply的思路基本是一致的,那区别是什么呢?

​ 区别就是call()方法接受的是参数列表,而apply()方法接受的是一个参数数组

  • 调用call:func.call(obj, param1, param2, param3)
  • 调用apply:func.apply(obj, [param1,param2,param3])
不过要注意除了数组, apply ()也可以接受类数组:
Function.prototype.apply = function apply(obj, params) {
    obj == null ? obj = window : null;
    !/^(function|object)$/i.test(typeof obj) ? obj = Object(obj) : null;
    //展开运算符不能把json类型的类数组转成数组对象,所以这里用Array.from
    params = Array.isArray(params)? params : Array.from(params);

    let key = Symbol('key');
    obj[key] = this;

    let result = obj[key](...params);
    delete obj[key];
    return result;
}


//测试一下
let test = {
    name: "test"
}

let json = {
    0: 'b1',
    1: 'b2',
    2: 'b3',
    length: 3
}

const func = function func(behavior1, behavior2, behavior3) {
    this.behavior1 = behavior1;
    this.behavior2 = behavior2;
    this.behavior3 = behavior3;
    return this;
}

let res = func.apply(test, json);
console.log(res);//=>{name: "test", behavior1: "b1", behavior2: "b2", behavior3: "b3"}

三、bind()

大体思路,以document.body.onclick = func.bind(obj, param1, param2, param3)为例:

  1. 执行bind()方法,并且bind的this是点前面的函数func
  2. !!不是立即执行func,而是利用闭包把func,obj,param1,param2,param3先储存起来
    • 要利用闭包 => 得创建一个不被释放的上下文 => 返回一个匿名函数,让它被引用 (比如想象有一个用完的盒子A,但是你又惊喜地发现盒子A里面有一个盒子B还有用。因为盒子B拿不出来,那为了继续使用B,是不是就不能扔掉A----函数执行上下文。同样的,那既然A没有被扔掉,里面的除了B以外的其他小东西----私有变量就都还在。)
    • 这样这些私有变量也能存起来啦
  3. 所以当使用bind()的时候,其实是把返回的匿名函数用来作事件绑定。
  4. 等触发事件的时候,再执行这个匿名函数。而匿名函数里又去执行func,并将func的this指向obj,再把参数传给func。
那就来看看代码吧~:
Function.prototype.bind = function bind(obj, ...params) {
    // bind()的this => 调用它的func
    return () => {
        //箭头函数是没有自己的this的,所以这里写this,它就会去上一级找this,也就能找到func了
        this.call(obj, ...params);
    }
}

//测试一下
let timer = null,
    test = {
        name: "test"
    }

const func = function func(a, b) {
    this.total = a + b;
    alert("test的total是" + this.total);
}

// document.body.onclick = func.bind(test, 1, 2);
clearTimeout(timer);
timer = setTimeout(func.bind(test, 1, 2), 1000);
接着来优化一下,可以给匿名函数传递一些参数(比如可以把事件传递过去):
Function.prototype.bind = function bind(obj, ...params) {
    // bind()的this => 调用它的func
    return (...args) => {
        //箭头函数是没有自己的this的,所以这里写this,它就会去上一级找this,也就能找到func了
        this.call(obj, ...params.concat(args));
    }
}

总结

call() Vs apply():接受的参数不一样

call() 和 apply () Vs bind():是否立即执行函数

//如果使用call的话,直接就执行func了
setTimeout(func.call(test, 1, 2), 1000); 
//而使用bind,只是预存好func和其他信息并返回匿名函数, 等1000毫秒到了再执行匿名函数,从而执行预设好的func
setTimeout(func.bind(test, 1, 2), 1000); 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值