JS 之 this 关键字

96 阅读3分钟

本文将介绍 this 关键字在函数、箭头函数上下文,以及修改this关键字的方法。

一、this 关键字调用方式

1、普通函数调用

function getThis() {
    console.log(this);// this === window
}

当函数作为普通函数调用时,this指向在严格模式和非严格模式下有所不同。严格模式下,this指向undefined,非严格模式下,this指向window

2、函数作为对象方法调用

const obj = {
    name: 'Lee',
    sayHi: function(){
        console.log(this)// this === {name: 'Lee', sayHi: ƒ}
    }
 }
 obj.sayHi()

当函数被作为对象的方法调用时,其中的 this 指向该对象本身。上述代码:sayHi函数作为obj的一个方法调用。所以this指向obj,而不是全局对象window。

3、构造函数调用

function Person(name, age){
    this.name = name;
    this.age = age;
    console.log(this);// this === Person {name: 'Lee', age: 22}
}

const person = new Person('Lee', 22);

通过 new 关键字创建实例时,this 关键字会指向新创建的对象。

4、箭头函数调用

const obj = {
  name: 'Lee',
  greetting: function() {
    const arrowFunc = () => {
     console.log(this);// this === {name: 'Lee', greetting: ƒ}
    };

    return arrowFunc();
  }
};
console.log(obj.greetting());



const obj = {
  name: 'Lee',
  sayHi: () => {
      console.log(this);// this === window
  }
};

obj.sayHi();

箭头函数的 this 绑定与常规函数不同,箭头函数没有自己的 this 值,而是捕获了封闭上下文的 this 值。所以上述代码中,箭头函数中的this引用的就是最近作用域中 this

5、函数调用时使用call或apply

const obj = {
    name: 'Lee'
}

function useCallOrApply(x, y){
     console.log(x, y);
     console.log(this);
}
useCallOrApply.call(obj, 1, 2);// this === obj 1、2 为参数
useCallOrApply.apply(obj, [1, 2]);// this === obj [1, 2] 为参数

通过使用函数的 call 或 apply 方法,可以显式地指定函数执行时的上下文,即 this 的值。在上述代码中,useCallOrApply.call(obj, 1, 2) 和 useCallOrApply.apply(obj, [1, 2]) 中的 this 都被绑定到了 obj 对象.

二、自定义修改this关键字函数

1、手写call

call():在使⽤⼀个指定的 this 值和若⼲个指定的参数值的前提下调⽤某个函数或⽅法。 call传入的参数不固定,第一个参数代表函数体内this指向,从第二个参数开始往后,每个参数被一次传入函数。

call 函数代码实现如下:

 Function.prototype.myCall = function(context) {
      // 判断调用对象是否为函数
      if(typeof this !== "function") throw new Error('调用对象应为函数!')
      // 判断context上下文是否存在;否的话,context设置为window
      if(typeof context === 'undefined' || context === null) context = window;
      // 获取参数
      let args = [...arguments].slice(1);
      let result = null;

      // 将函数作为上下文对象的一个属性。
      context.fn = this;
      // 调用函数
      result = context.fn(...args);
      // 删除属性
      delete context.fn;
      return result;
    }

    const obj = {
      name: 'Lee',
      isWorking: true,
    }
    function getUserWork(x, y) {
      console.log(x, y);
      console.log(this);
    }
    getUserWork.myCall(obj, 2, 4);// this === obj
    getUserWork.myCall();// this === window
    getUserWork.myCall(null, 2, 4);// this === window

2、手写apply

apply 的实现跟 call 类似,只是⼊参不⼀样,apply为数组;

apply 函数代码实现如下:

 Function.prototype.myApply = function(context) {
      // 判断调用对象是否为函数
      if(typeof this !== "function") throw new Error('调用对象应为函数!')
      // 判断context上下文是否存在;否的话,context设置为window
      if(typeof context === 'undefined' || context === null) context = window;
      
      // 获取参数
      let result = null;

      context.fn = this;
      // 判断是否传参
      result = arguments[1] ? context.fn(...arguments[1]) : context.fn();
      // 删除属性
      delete context.fn;
      return result;
    }

    const obj = {
      name: 'Lee',
      isWorking: true,
    }
    function getUserWork(x, y) {
      console.log(x, y);
      console.log(this);
    }
    getUserWork.myApply(obj, [2, 4]);// this === obj
    getUserWork.myApply();// this === window
    getUserWork.myApply(null, [2, 4]);// this === window

3、手写bind

bind() ⽅法会创建⼀个新函数。当这个新函数被调⽤时,bind() 的第⼀个参数将作为它运⾏时的 this,之后的⼀序列参数将会在传递的实参前传⼊作为它的参数。

bind 函数代码实现如下:

Function.prototype.myBind = function(context) {
      // 判断调用对象是否为函数
      if(typeof this !== "function") throw new Error('调用对象应为函数!')
      // 判断context上下文是否存在;否的话,context设置为window
      if(typeof context === 'undefined' || context === null) context = window;
      
      const fn = this;
      // 调用apply来绑定函数调用,返回一个函数
      return function(...args) {
        return fn.apply(context, args);
      }
    }

    const obj = {
      name: 'Lee',
      isWorking: true,
    }
    function getUserWork(x, y) {
      console.log(x, y);
      console.log(this);
    }
    const mybind1 = getUserWork.myBind(obj, [2, 4]);// this === obj
    mybind1();
    const mybind2 = getUserWork.myBind();// this === window
    mybind2();
    const mybind3 = getUserWork.myBind(null, 2, 4);// this === window
    mybind3();
    const mybind4 = getUserWork.myBind(obj);// this === obj
    mybind4();