对JS this、call、apply、bind的理解

108 阅读3分钟

this的理解

this指向最后一次调用这个方法的对象。
this,可以通过四种模式来判断:

  • 函数调用模式:当一个函数不是对象属性,直接作为函数调用时,this指向全局。
  • 方法调用模式:如果一个函数作为对象的方法调用,那么this指向这个对象。
  • 构造器模式:如果一个函数用new调用,函数执行前会创建一个新对象,this指向这个新创建的对象。
  • applycallbind调用模式:这三个方法都可以显式的指定this方向。其中apply方法只接受两个参数,第一个参数是this绑定的对象,第二个是参数数组。call方法接收多个参数,第一个是this绑定的对象,后面都是函数执行的参数。

applycallbind三者的异同

  • 都可以改变函数的this指向
  • 第一个参数都是this要指向的对象,如果没有这个参数或者这个参数为undefinednull,则默认指向全局的window
  • apply直接收两个参数,第一个参数是this要指向的对象,第二个是一个数组,以数组的形式进行传参;而call接收多个参数,除了第一个是this指向的对象外其他的都是要传入的参数。且两个都是一次传参,然后立即执行。
  • bind可以分为多次传参,返回的是绑定好this之后的函数。

三者的实现

call的实现

实现步骤:

  1. 首先判断调用ownCall方法的是不是一个函数对象
  2. 获取要传入的参数列表
  3. 判断this指向的是否为一个对象
  4. 通过symbol()定义一个唯一属性,并让this要指向的对象拥有改属性并且把调用ownCall 的方法设置为改属性的值
  5. 调用这个属性方法然后删除改属性并返回结果
Function.prototype.ownCall = function(context) {
    //判断调用ownCall方法的是否是一个函数
    if (typeof this !== 'function') {
        console.log("type error");
    }
    //获取参数
    let args = [...arguments].slice(1);
    //判断this的指向是否为一个对象类型
    context = (typeof context === 'object' ? context : window)
    // 防止覆盖掉原有属性
    const key = Symbol()
    // 这里的this为需要执行的方法
    context[key] = this
    // 方法执行
    const result = context[key](...args)
    delete context[key]
    return result
}

apply的实现

Call的实现方式差不多,不同之处在于给调用ownApply方法传参时要判断伪数组下表为1的元素是否存在且是否为伪数组。

Function.prototype.ownApply = function(context) {
    //判断调用ownCall方法的是否是一个函数
    if (typeof this !== 'function') {
        console.log("type error");
    }
    //获取参数
    //判断this的指向是否为一个对象类型
    context = (typeof context === 'object' ? context : window)
    // 防止覆盖掉原有属性
    const key = Symbol()
    // 这里的this为需要执行的方法
    context[key] = this
    // 方法执行
    let result
    if (arguments[1] && Array) {
        result = context[key](...arguments[1])
    } else {
        result = context[key]();
    }
    delete context[key]
    return result
}

bind实现

  1. 判断调用对象是否为函数
  2. 获取其余传入的值
  3. 将调用mybind函数在内部保存
  4. 创建一个函数返回
  5. 返回的函数内部使用apply来绑定函数调用
Function.prototype.myBind = function(context) {
    // 判断调用对象是否为函数
    if (typeof this !== "function") {
        throw new TypeError("Error");
    }
    // 获取参数
    var args = [...arguments].slice(1),
    fn = this;
    return function Fn() {
        // 根据调用方式,传入不同绑定值
        return fn.apply(
            this instanceof Fn ? this : context,
            args.concat(...arguments)
        );
    };
};