JavaScript笔试之实现call,apply,bind

85 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

实现一个call

call做了什么:

  • 将函数设为对象的属性
  • 执行&删除这个函数
  • 指定this到函数并传入给定参数执行函数
  • 如果不传入参数,默认指向为 window
// 模拟 call bar.mycall(null);
//实现一个call方法
Function.prototype.myCall = function(context) { 
    //此处没有考虑context非object情况 
    context.fn = this; 
    let args = []; 
    for (let i = 1, len = arguments.length; i < len; i++) {
        args.push(arguments[i]);
    } 
    context.fn(...args); 
    let result = context.fn(...args);
    delete context.fn; 
    return result;
}

具体参考JavaScript深入之call和apply的模拟

实现实现apply方法

对于apply和call两者在作用上是相同的,即是调用一个对象的一个方法,以另一个对象替换当前对象。将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。

// 模拟 apply apply原理与call很相似,不多赘述
Function.prototype.myapply = function(context, arr) { 
    var context = Object(context) || window; 
    context.fn = this; var result; 
    if (!arr) { 
        result = context.fn(); 
    } else { 
        var args = []; 
        for (var i = 0, len = arr.length; i < len; i++) { 
            args.push("arr[" + i + "]");
        } 
        result = eval("context.fn(" + args + ")");
    } 
    delete context.fn; 
    return result;
};

实现bind

实现bind要做什么

  • 返回一个函数,绑定this,传递预置参数
  • bind返回的函数可以作为构造函数使用。故作为构造函数时应使得this失效,但是传入的参数依然有效
// mdn的实现
if (!Function.prototype.bind) { 
    Function.prototype.bind = function(oThis) {
        if (typeof this !== 'function') { 
        // closest thing possible to the ECMAScript 5 
        // internal IsCallable function 
        throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }
    var aArgs = Array.prototype.slice.call(arguments, 1), 
        fToBind = this, 
        fNOP = function() {}, 
        fBound = function() {
            // this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用 
        return fToBind.apply(this instanceof fBound 
                ? this
                : oThis, 
                // 获取调用时(fBound)的传参.bind 返回的函数入参往往是这么传递的 
                aArgs.concat(Array.prototype.slice.call(arguments)));
        }; 
    // 维护原型关系 
    if (this.prototype) { 
        // Function.prototype doesn't have a prototype property 
        fNOP.prototype = this.prototype;
    } 
    // 下行的代码使fBound.prototype是fNOP的实例,因此 
    // 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例 
    fBound.prototype = new fNOP(); 
        return fBound;
    };
}

详解JavaScript深入之bind的模拟实现

总结Call和apply,bind的区别?

  • call方法调用一个函数,其具有一个指定的this值和分别地提供的参数(参数的列表)。注意:该方法的作用和apply()方法类似,只有一个区别,就是call()方法接受的是若干个参数的列表,而apply()方法接受的是一个包含多个参数的数组
  • 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。注意:call()方法的作用和apply()方法类似,区别就是call()方法接受的是参数列表,而apply()方法接受的是一个参数数组
  • bind()方法创建一个新的函数,当这个新的函数被调用时,其this置为提供的值,其参数列表前几项,置为创建时指定的参数序列
  • 三者都可以把一个函数应用到其他对象上,call、apply是修改函数的作用域(修改this指向),并且立即执行,而bind是返回了一个新的函数,不是立即执行.apply和call的区别是apply接受数组作为参数,而call是接受逗号分隔的无限多个参数列表,