【js八股文】this值大揭秘

54 阅读4分钟

在 JavaScript 中,this 是一个关键字,其值取决于函数的调用方式。理解 this 的行为对于编写健壮和可维护的代码至关重要。本文将深入探讨 this 的不同情况,并特别关注其在定时器(如 setTimeout 和 setInterval)中的应用。

1. 基本概念

1.1 全局上下文

在全局执行上下文中,this 指向全局对象。在浏览器中,全局对象是 window


console.log(this); // 输出: Window {...}

1.2 函数上下文

在函数内部,this 的值取决于函数的调用方式。

  • 简单调用:如果函数是直接调用的,this 指向全局对象(在严格模式下为 undefined)。

    function simpleFunction() {
      console.log(this);
    }
    
    simpleFunction(); // 输出: Window {...} (非严格模式)
    simpleFunction(); // 输出: undefined (严格模式)
    
  • 作为对象方法调用:如果函数是作为对象的方法调用的,this 指向调用该方法的对象。

    
    const obj = {
      name: 'Alice',
      sayHello: function() {
        console.log(`Hello, ${this.name}!`);
      }
    };
    
    obj.sayHello(); // 输出: Hello, Alice!
    
  • 作为构造函数调用:如果函数是通过 new 关键字调用的,this 指向新创建的对象。

    
    function Person(name) {
      this.name = name;
    }
    
    const person = new Person('Bob');
    console.log(person.name); // 输出: Bob
    
  • 指定 this 调用方式:使用 callapply 和 bind 方法可以显式地设置 this 值。

    
    function greet() {
      console.log(`Hello, ${this.name}!`);
    }
    
    const person = { name: 'Charlie' };
    
    greet.call(person); // 输出: Hello, Charlie!
    greet.apply(person); // 输出: Hello, Charlie!
    
    const greetCharlie = greet.bind(person);
    greetCharlie(); // 输出: Hello, Charlie!
    
  • 箭头函数:箭头函数没有自己的 this 绑定,它会捕获其所在上下文的 this 值。

    const obj = {
      name: 'David',
      sayHello: function() {
        const innerFunction = () => {
          console.log(`Hello, ${this.name}!`);
        };
        innerFunction();
      }
    };
    
    obj.sayHello(); // 输出: Hello, David!
    
  • 练习

"use strict";
var x = 2;
var obj = {
    x: 1,
    foo: function() {
      console.log(this);
      console.log(this.x);
    }
  }
  var foo = obj.foo;
  var obj2 = {
  x: 5,
  foo: foo
};
obj2.foo(); 
obj.foo();  
foo(); 

  • 分析
  • x 是一个全局变量,值为 2
  • obj 是一个对象,包含一个属性 x,值为 1,以及一个方法 foo
  • foo 现在引用了 obj 对象中的 foo 方法。
  • obj2.foo() 调用时,this 指向 obj2,因此 this.x 的值为 5
  • obj.foo() 调用时,this 指向 obj,因此 this.x 的值为 1
  • 在严格模式下,直接调用 foo() 时,this 是 undefined,因为没有对象来调用这个函数。
  • 因此,this.x 会输出 undefined
  • 输出
{ x: 5, foo: [Function: foo] }  5
 { x: 1, foo: [Function: foo] }  1
 {}  undefined

2. 定时器中的 this

定时器函数 setTimeout 和 setInterval 用于延迟执行代码。理解 this 在这些函数中的行为对于避免常见的错误非常重要。

2.1 普通函数在 setTimeout 中

当普通函数在 setTimeout 的回调中执行时,this 指向全局对象(非严格模式)或 undefined(严格模式)。


var name = "周杰伦";
var a = {
    name: "薛之谦",
    func1: function() {
        console.log(this.name);
    },
    fun2: function() {
        setTimeout(function() {
            this.func1(); // this 指向全局对象(非严格模式)或 undefined(严格模式)
        }, 1000);
    }
};

a.fun2();

输出(非严格模式):

Uncaught TypeError: this.func1 is not a function

输出(严格模式):

Uncaught TypeError: Cannot read property 'func1' of undefined

解释

  • 在 fun2 方法中,setTimeout 被调用,并传入一个普通函数。
  • 这个普通函数在 setTimeout 的回调中执行。
  • 在 setTimeout 的回调中,this 指向全局对象(非严格模式)或 undefined(严格模式)。
  • 因此,this.func1() 会报错,因为全局对象(或 undefined)没有 func1 方法。

2.2 箭头函数在 setTimeout 中

箭头函数没有自己的 this 绑定,它会捕获其所在上下文的 this 值。


var name = "刀郎";
var a = {
    name: "薛之谦",
    func1: function() {
        console.log(this.name);
    },
    fun2: function() {
        setTimeout(() => {
            this.func1(); // this 指向对象 a
        }, 1000);
    }
};

a.fun2();

输出

薛之谦

解释

  • 在 fun2 方法中,setTimeout 被调用,并传入一个箭头函数。
  • 箭头函数没有自己的 this 绑定,它会捕获其所在上下文的 this 值。
  • 在 fun2 方法中,this 指向对象 a,因为 fun2 是作为对象 a 的方法被调用的。
  • 因此,箭头函数中的 this 也指向对象 a
  • this.func1() 调用了对象 a 的 func1 方法,输出 "薛之谦"

2.3 使用 callapply 和 bind 指定 this

你可以使用 callapply 和 bind 方法来显式地设置 this 值。

使用 bind

var name = "刀郎";
var a = {
    name: "薛之谦",
    func1: function() {
        console.log(this.name);
    },
    fun2: function() {
        setTimeout(this.func1.bind(this), 1000);
    }
};

a.fun2();

输出

薛之谦

解释

  • 在 fun2 方法中,setTimeout 被调用,并传入 this.func1.bind(this)
  • bind 方法返回一个新的函数,并永久地绑定 this 值为对象 a
  • 因此,setTimeout 的回调函数中的 this 指向对象 a
  • this.func1() 调用了对象 a 的 func1 方法,输出 "薛之谦"

总结

  • 对象的方法调用this 指向调用该方法的对象。
  • 普通函数调用this 指向全局对象(非严格模式)或 undefined(严格模式)。
  • 构造函数调用this 指向新创建的对象。
  • 指定 this 调用方式:使用 callapply 和 bind 方法可以显式地设置 this 值。
  • 箭头函数:箭头函数没有自己的 this 绑定,它会捕获其所在上下文的 this 值。