实现Call、Apply、Bind

83 阅读2分钟

手写一个Call函数

1、call的实现

定义:
call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。
使用方法:
const foo = {
  name: 'kaiwen'
}
const getName = function() {
 console.log(this.name)
}
getName.call(foo) // kaiwen

可以看出:call改变了this的指向并执行了getName

实现的思路:

首先,我们可以把getName加到foo对象当中,这样就完成了this的绑定,然后在函数内部执行getName,执行完成后为了避免影响对象,再用delete把该函数删掉。

实现代码:

Function.prototype.myCall = function(obj) {
  obj.fn = this // this指代当前函数
  const res = obj.fn() // 执行函数返回结果
  delete obj.fn // 删除函数
  return res
}

这样就完成了一个简易版的call,但call应该要支持传参,例如:

const foo = {
  name: 'kaiwen'
}
const getName = function(age) {
 console.log(this.name)
 console.log(age)
}
getName.call(foo, 19) // kaiwen 19

这里可以选择es6...运算符。

Function.prototype.myCall = function (params, ...args) {
    // 获取函数
   params['fn'] = this
   const res = params.fn(...args)
   // 删除函数
   delete params['fn']
   return res
}

es3的实现方法:

Function.prototype.call2 = function(context) {
    context.fn = this;
    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']');
    }
    const res = eval('context.fn(' + args +')');
    delete context.fn;
    return res
}

要注意一下this的值可以为null,当为null的时候指向window。

最终版:
Function.prototype.call2 = function (context) {
    var context = context || window;
    context.fn = this;

    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']');
    }

    var result = eval('context.fn(' + args +')');

    delete context.fn
    return result;
}

参考文章:JavaScript深入之call和apply的模拟实现

手写一个Apply

1、apply与call的区别:主要是在传参上有区别,apply需要的参数为一个数组。这里直接给出参考代码。

Function.prototype.apply = 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

Function.prototype.bind = function(){
  const fn = this, // 保存函数
        args = Array.prototype.slice.call(arguments), // 参数
        object = args.shift();  //绑定对象
  return function(){
    return fn.apply(object,args.concat(Array.prototype.slice.call(arguments)));
  };
};