美文网首页
call、apply、bind三者之间的用法和区别,并手写实现

call、apply、bind三者之间的用法和区别,并手写实现

作者: 只跟自己比 | 来源:发表于2020-06-25 21:34 被阅读0次

    call、apply、bind 的用法

    1. 调用方法

    let xw = {
        name : '小王',
        gender : '男',
        age : 24 ,
        say : function() {
            console.log(this.name + ',性别' + this.gender + ',年龄' + this.age);
        }
    }
    let xh = {
        name : '小红',
        gender : '女',
        age : 20
    }
    let xl = {
        name : '小蓝',
        gender : '男',
        age : 22
    }
    xw.say();  // this还是原来的xw的
    xw.say.call(xh);   // this指向xh
    xw.say.apply(xl);  // this指向xl
    xw.say.bind(xh)(); // this指向xh
    

    2. 传参方法

    let xw = {
        name : '小王',
        gender : '男',
        age : 24 ,
        say : function(school , grade) {
            console.log(this.name + ',性别' + this.gender + ',年龄' + this.age + ',在' + school + "上" + grade);
        }
    }
    let xh = {
        name : '小红',
        gender : '女',
        age : 20
    }
    let xl = {
        name : '小蓝',
        gender : '男',
        age : 22
    }
    xw.say("实验小学","六年级");
    xw.say.call(xh , "实验小学","六年级");
    xw.say.apply(xl , ["实验小学","六年级"]);
    xw.say.bind(xh,"实验小学","六年级")();
    xw.say.bind(xl)("实验小学","六年级");
    

    call、apply、bind 的区别

    callapplybind 的共同点都是为了解决改变 this 的指向。作用都是相同的,只是传参的方式不同。

    不同点:

    • call()apply() 是立即执行的,而 bind() 是返回一个函数。
    • call() 可以传递多个参数,第一个参数和 apply() 一样,是用来替换的对象,后面是参数列表。
    • apply() 最多只能有两个参数 —— 新this 对象和一个参数数组 argArray
    • bind() 和其他两个方法的作用也是一致的,只是该方法会返回一个函数,并且可以通过bind() 实现 柯里化

    柯里化会在下一篇文章进行较为详细的介绍。

    手写实现call()、apply()、bind() 函数

    对于实现以下三个函数,可以从这几个方面进行考虑:

    • 不传入第一个参数,那么默认为 window
    • 改变了 this 指向,让新的对象可以执行该函数。那么思路就可以变成给新的对象添加一个函数,然后在执行完以后删除。

    手写实现 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'
    }
    function fn(name , age){
        return {
            value : this.value ,
            name , 
            age
        }
    }
    let res = fn.myCall(obj , 'LL' , 25);
    console.log(res) // { value: 'hello', name: 'LL', age: 25 }
    

    手写实现 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()

    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)
    

    相关文章

      网友评论

          本文标题:call、apply、bind三者之间的用法和区别,并手写实现

          本文链接:https://www.haomeiwen.com/subject/egjefktx.html