跳转到内容

this 指向

在 JavaScript 中,this 是一个特殊的关键字,它指向当前执行上下文中的对象。理解 this 的指向是掌握 JavaScript 的关键之一,因为它的值在不同的执行环境中会发生变化。

this 的基本概念

this 关键字总是指向一个对象,这个对象是在函数运行时基于执行上下文动态确定的。在全局执行上下文中,this 指向全局对象;在函数执行上下文中,this 的指向取决于函数的调用方式。

this 的指向规则

1. 全局环境中的 this

在全局执行上下文中(任何函数外部),无论是否在严格模式下,this 都指向全局对象。在浏览器中,全局对象是 window

js
console.log(this === window); // true

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

globalFunction();

2. 函数调用中的 this

在非严格模式下,普通函数调用中的 this 指向全局对象。在严格模式下,thisundefined

js
function normalFunction() {
  console.log(this);
}

normalFunction(); // 非严格模式下是 window,严格模式下是 undefined

// 严格模式示例
function strictFunction() {
  'use strict';
  console.log(this); // undefined
}

strictFunction();

3. 对象方法中的 this

当函数作为对象的方法被调用时,this 指向调用该方法的对象。

js
const person = {
  name: '张三',
  greet: function() {
    console.log(this.name); // '张三'
    console.log(this === person); // true
  }
};

person.greet();

需要注意的是,this 的绑定是在运行时确定的,而不是在定义时:

js
const person1 = {
  name: '张三',
  greet: function() {
    console.log(this.name);
  }
};

const person2 = {
  name: '李四'
};

person2.greet = person1.greet;
person2.greet(); // 输出 '李四',而不是 '张三'

4. 构造函数中的 this

当使用 new 关键字调用函数时,会创建一个新的对象,此时 this 指向新创建的对象。

js
function Person(name) {
  this.name = name;
  this.greet = function() {
    console.log('Hello, I am ' + this.name);
  };
}

const person = new Person('张三');
person.greet(); // Hello, I am 张三

5. 箭头函数中的 this

箭头函数不绑定自己的 this,而是继承外层作用域的 this 值。

js
const obj = {
  name: '张三',
  regularFunction: function() {
    console.log('regularFunction:', this.name);
    
    const arrowFunction = () => {
      console.log('arrowFunction:', this.name);
    };
    
    arrowFunction();
  }
};

obj.regularFunction();
// regularFunction: 张三
// arrowFunction: 张三

在全局作用域中定义的箭头函数中的 this 指向全局对象:

js
const globalArrowFunction = () => {
  console.log(this === window); // true
};

globalArrowFunction();

6. 事件处理器中的 this

在 DOM 事件处理器中,this 指向绑定事件的元素:

js
document.querySelector('button').addEventListener('click', function() {
  console.log(this); // 指向 button 元素
});

显式绑定 this

JavaScript 提供了几种方法来显式地设置函数执行时的 this 值:

call() 方法

call() 方法调用一个函数,第一个参数作为 this 值,其余参数作为函数的参数:

js
function greet() {
  console.log('Hello, I am ' + this.name);
}

const person = { name: '张三' };
greet.call(person); // Hello, I am 张三

apply() 方法

apply() 方法与 call() 类似,但参数以数组形式传递:

js
function introduce(age, city) {
  console.log(`我是 ${this.name},今年 ${age} 岁,来自 ${city}`);
}

const person = { name: '张三' };
introduce.apply(person, [25, '北京']); 
// 我是 张三,今年 25 岁,来自 北京

bind() 方法

bind() 方法创建一个新函数,将 this 按照指定值绑定,并可以预设部分参数:

js
function greet() {
  console.log('Hello, I am ' + this.name);
}

const person = { name: '张三' };
const boundGreet = greet.bind(person);
boundGreet(); // Hello, I am 张三

常见陷阱和注意事项

1. 方法赋值给变量

当将对象的方法赋值给变量后,方法中的 this 会丢失绑定:

js
const obj = {
  name: '张三',
  greet: function() {
    console.log(this.name);
  }
};

const greetFunc = obj.greet;
greetFunc(); // undefined (非严格模式) 或 TypeError (严格模式)

解决方法是使用 bind()

js
const boundGreet = obj.greet.bind(obj);
boundGreet(); // 张三

2. 回调函数中的 this

在回调函数中,this 通常不会按预期指向原对象:

js
const obj = {
  name: '张三',
  delayedGreet: function() {
    setTimeout(function() {
      console.log(this.name); // undefined
    }, 1000);
  }
};

obj.delayedGreet();

可以使用箭头函数解决:

js
const obj = {
  name: '张三',
  delayedGreet: function() {
    setTimeout(() => {
      console.log(this.name); // 张三
    }, 1000);
  }
};

obj.delayedGreet();

或者使用 bind()

js
const obj = {
  name: '张三',
  delayedGreet: function() {
    setTimeout(function() {
      console.log(this.name); // 张三
    }.bind(this), 1000);
  }
};

obj.delayedGreet();

总结

理解 JavaScript 中 this 的指向规则对于编写正确的代码至关重要。记住以下几点:

  1. this 的值是在运行时确定的,不是在定义时
  2. 在全局作用域中,this 指向全局对象
  3. 在对象方法中,this 指向调用该方法的对象
  4. 在构造函数中,this 指向新创建的对象
  5. 箭头函数不绑定 this,而是继承外层作用域的 this
  6. 可以使用 call()apply()bind() 显式设置 this