【每日面试题】手写call, apply, bind,以及说出他们的区别

104 阅读1分钟

call、apply、bind 相同点

都可以用来改变 this 对象的指向,第一个参数都是 this 要指向的对象,也就是想指定的上下文,三者都可以利用后续参数传参。

call、apply、bind 不同点

apply 和 call 传参方式不同,apply 第二个参数是数组,call 则是 arg1,arg2...这种形式。

bind 是改变 this 作用域会返回一个新的函数,这个函数不会马上执行。参数可以拼接。

function fn () {
    console.log(arguments) // [1, 2, 3, 4]
}

var obj = {}

// call 使用
fn.call(obj, 1, 2, 3, 4)

// apply 使用 
fn.apply(obj, [1, 2, 3, 4])

// bind 使用
var newFn = fn.bind(obj, 1, 2)
newFn(3, 4)

实现 call 方法

思路

  • 将函数设为对象的属性
  • 执行 & 删除这个函数
  • 指定 this 到函数,并传入给定参数执行函数
  • 如果不传入参数,默认指向 window
Function.prototype.myCall = function (context) {
    var context = context || window;
    context.fn = this;

    var args = [];
    if (arguments.length > 1) {
        for (var i = 1; i < arguments.length; i++) {
            args.push(arguments[i])
        }
    }

    var result = context.fn(...args)

    delete context.fn;
    return result
}

实现 apply 方法

Function.prototype.myApply = function(context, arr) {
    var context = context || window;
    context.fn = this;

    if (!arr.length) {
        return context.fn();
    } else {
        var result = context.fn(...arr);

        delete context.fn;
        return result;
    }
}

实现 bind 方法

思路

  • 返回一个函数,并绑定this,传递预设的参数
  • bind 返回的函数可以作为构造函数使用。故作为构造函数时应使得 this 失效,但是传入的参数依然有效
Function.prototype.myBind = function (context) {
    if (typeof this !== 'function') {
        throw Error('this is not a function')
    }

    var context = context || window;
    var Fn = function () {}
    var _this = this;
    var args = Array.prototype.slice.call(arguments, 1);

    var result = function () {
        return _this.apply(
            this instanceof Fn ? this : context,
            args.concat(...arguments)
        )
    }

    if (this.prototype) Fn.prototype = this.prototype

    result.prototype = new Fn()

    return result;
}