Skip to content
On This Page

JavaScript_this

1. 引言

this 是什么:this 是 JavaScript 中的一个关键字,它指向当前执行上下文中的一个对象。与其他语言(如 Java、C++)不同,JavaScript 中的 this 不是在函数定义时确定的,而是在函数调用时动态绑定的

2. 四大绑定规则

根据函数的调用方式不同,this 会绑定到不同的对象。主要有以下四种规则。

2.1 默认绑定

独立函数调用时,默认绑定规则生效。

  • 非严格模式下,this 指向全局对象(浏览器中是 window,Node.js 中是 global)。
  • 严格模式'use strict')下,thisundefined
javascript
function showThis() {
  console.log(this);
}
showThis();  // 非严格: window,严格: undefined

// 函数内部嵌套的函数也是独立调用
function outer() {
  function inner() {
    console.log(this);
  }
  inner();  // 独立调用,仍然是默认绑定
}
outer();

2.2 隐式绑定

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

javascript
const person = {
  name: 'Alice',
  greet() {
    console.log(`Hello, I'm ${this.name}`);
  }
};
person.greet(); // Hello, I'm Alice

常见陷阱:隐式丢失

如果把方法赋值给一个变量,然后单独调用,就会丢失隐式绑定,回退到默认绑定:

javascript
const greetFn = person.greet;
greetFn();  // Hello, I'm undefined(非严格下输出 Hello, I'm undefined,因为 this 指向 window,window.name 通常为空)

同样,作为回调函数传递时也会丢失,回退到默认绑定:

javascript
setTimeout(person.greet, 100); // 回调执行时是独立调用,丢失 this

2.3 显式绑定

通过 callapplybind 可以强制指定函数执行时的 this

  • call(thisArg, arg1, arg2, ...):立即执行,参数逐个传递。
  • apply(thisArg, [arg1, arg2, ...]):立即执行,参数以数组形式传递。
  • bind(thisArg, arg1, arg2, ...):返回一个新函数,this 被永久绑定,可延迟执行。
javascript
function introduce(age, city) {
  console.log(`${this.name}, ${age}岁, 来自${city}`);
}
const user = { name: 'Bob' };

introduce.call(user, 25, '北京');      // Bob, 25岁, 来自北京
introduce.apply(user, [25, '北京']);   // Bob, 25岁, 来自北京

const bound = introduce.bind(user, 25);
bound('上海');                         // Bob, 25岁, 来自上海

2.4 new 绑定

使用 new 关键字调用构造函数时,会执行以下步骤:

  1. 创建一个全新的空对象。
  2. 将这个空对象的原型指向构造函数的 prototype 属性。
  3. 将这个空对象绑定为构造函数内部的 this
  4. 如果构造函数返回非原始值,则返回该值;否则返回新创建的对象。

因此,new 调用后,this 指向新创建的对象实例

javascript
function Car(model) {
  this.model = model;
  this.drive = function() {
    console.log(`Driving ${this.model}`);
  };
}
const tesla = new Car('Model 3');
tesla.drive(); // Driving Model 3

3. 箭头函数:无自己的 this

箭头函数(=>)不绑定自己的 this,它捕获其所在(定义时)上下文的 this作为自己的 this。并且,箭头函数的 this 无法通过 callapplybind 改变(但可以传入参数)。

javascript
const obj = {
  name: 'Arrow',
  normal: function() {
    console.log('normal:', this.name);
  },
  arrow: () => {
    console.log('arrow:', this.name);
  }
};
obj.normal(); // normal: Arrow
obj.arrow();  // arrow: undefined(因为箭头函数定义时 this 指向全局)

注意:在对象字面量中定义箭头函数,其 this 通常指向全局(浏览器中为 window),而不是对象本身。因此,对象方法一般不建议使用箭头函数。

箭头函数最适合用于回调函数,避免丢失外层 this

javascript
function Timer() {
  this.seconds = 0;
  setInterval(() => {
    this.seconds++;   // 箭头函数中的 this 指向 Timer 实例
    console.log(this.seconds);
  }, 1000);
}
new Timer();

4. 绑定优先级与例外情况

4.1 优先级

当多种绑定规则同时适用时,优先级从高到低为:

new 绑定 > 显式绑定(call/apply/bind) > 隐式绑定 > 默认绑定

javascript
function foo(something) {
  this.a = something;
}
const obj1 = {};
const bar = foo.bind(obj1);
bar(2);
console.log(obj1.a); // 2

const obj2 = new bar(3);
console.log(obj1.a); // 2 (没有被修改)
console.log(obj2.a); // 3 (new 绑定的优先级高于显式绑定)

4.2 例外情况

  • 忽略 this:如果显式绑定传入 nullundefinedthis 会被忽略,使用默认绑定。这种做法不推荐(可能意外修改全局对象),可使用 Object.create(null) 代替。

    javascript
    function test() {
      console.log(this);
    }
    test.call(null); // 非严格: window,严格: null
  • 间接引用(0, obj.func)() 这种写法会使函数独立调用,导致默认绑定。

    javascript
    var name = 'global';
    const obj = {
      name: 'local',
      getName() { console.log(this.name); }
    };
    (obj.getName)();     // local(括号不影响)
    (0, obj.getName)();  // global(间接引用,相当于独立调用)

5. 特殊场景下的 this

5.1 事件处理函数

  • 传统函数(非箭头函数)作为 DOM 事件监听器时,this 指向触发事件的元素。
  • 箭头函数中的 this 保持定义时的外层 this(通常是 window 或所在组件)。
javascript
<button id="btn">Click</button>
<script>
  const btn = document.getElementById('btn');
  btn.addEventListener('click', function() {
    console.log(this); // <button id="btn">...
  });
  btn.addEventListener('click', () => {
    console.log(this); // window(严格模式下 undefined)
  });
</script>

5.2 定时器 setTimeout / setInterval

回调函数默认情况下是独立调用,因此 this 指向全局对象(严格模式为 undefined)。

javascript
const obj = {
  name: 'Timer',
  say() {
    setTimeout(function() {
      console.log(this.name); // undefined 或 ''
    }, 100);
  }
};
obj.say();

解决方案:使用箭头函数、bind 或者保存 this 到变量(const self = this)。

5.3 类(class)中的 this

ES6 类的方法默认采用严格模式。将类方法作为回调传递时,会丢失 this 指向。

javascript
class Logger {
  constructor(name) {
    this.name = name;
  }
  log() {
    console.log(this.name);
  }
}
const logger = new Logger('App');
setTimeout(logger.log, 100); // 丢失 this → undefined

三种修复方式:

javascript
// 1. 在构造函数中 bind
class Logger {
  constructor(name) {
    this.name = name;
    this.log = this.log.bind(this);
  }
  log() { console.log(this.name); }
}

// 2. 使用箭头函数作为类字段(需要 Babel 或现代浏览器)
class Logger {
  log = () => {
    console.log(this.name);
  }
}

// 3. 调用时显式绑定
setTimeout(() => logger.log(), 100);

5.4 严格模式的影响

严格模式下,默认绑定的 this 变成 undefined,而不是全局对象。这可以避免很多意外错误。

javascript
'use strict';
function test() {
  console.log(this);
}
test(); // undefined