JavaScript中this关键字的四种调用模式与箭头函数详解

作者:函数茶馆时间:2026-10-30 11:14:56浏览:326

本文详细解析JavaScript中this关键字的动态绑定机制,涵盖对象方法调用、函数直接调用、new构造函数调用以及call/apply/bind明确指定上下文四种情况。同时对比传统函数与箭头函数的this差异,指出箭头函数静态绑定的特性及其在方法定义中的潜在陷阱,帮助开发者准确掌握this的使用规则。

this关键字是JavaScript中常被讨论的话题。尽管相关教程众多,但在实际项目开发中仍可能遇到困惑。通过参考JavaScript weekly推荐的详解文章及中文译文,我对this的理解得到了进一步加深。

this的动态绑定机制

JavaScript中的this是动态绑定的,其值在函数运行时确定,而非声明时。任何函数都可以访问this,无论它是否属于某个特定对象。理解this主要涉及以下四种调用场景。

对象方法调用

1. 作为对象方法调用

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


  var john = {
   firstName: "John"
  }
  function func() {
   alert(this.firstName + ": hi!")
  }
  john.sayHi = func
  john.sayHi() // this = john

需要注意的是,如果将对象的方法提取并赋值给变量,该调用方式变为普通函数调用,此时this指向全局对象window(非严格模式)或undefined(严格模式)。

函数直接调用

2. 函数直接调用

当函数中有 this,其实就意味着它被当做方法调用,之间调用相当于把他当做window对象的方法,this指向window,值得注意的是ES5其实是规定这种情况this=undefined的,只浏览器大多还是按照老的方法执行(本人在最新版的Chrome,Safari,Firefox中测试都指向window(201607)),在火狐下使用严格模式指向undefined;


  func()
  function func() {
    alert(this) // [object Window] or [object global] or kind of..
  }

为了正确传递this,函数调用前的表达式必须是引用类型,例如obj.a或obj['a'],不能是其他形式。

这里存在一个常见误区:当对象的方法内部包含另一个函数时,该内部函数以函数模式触发,其this默认指向window(严格模式下为undefined)。解决方法是手动为该内部函数绑定this。


var numbers = { 
  numberA: 5,
  numberB: 10,
  sum: function() {
   console.log(this === numbers); // => true
   function calculate() {
    // this is window or undefined in strict mode
    console.log(this === numbers); // => false
    return this.numberA + this.numberB;
   }
   return calculate();
  }
};
numbers.sum(); // => NaN or throws TypeError in strict mode 
var numbers = { 
  numberA: 5,
  numberB: 10,
  sum: function() {
   console.log(this === numbers); // => true
   function calculate() {
    console.log(this === numbers); // => true
    return this.numberA + this.numberB;
   }
   // use .call() method to modify the context
   return calculate.call(this);
  }
};
numbers.sum(); // => 15 

构造函数调用

3. 在new构造函数中调用

引用对象的变量实际上保存的是对该对象数据的指针。使用new关键字时,this的绑定过程包含以下步骤:

首先创建this = {}; 接着在new执行过程中可能修改this,并添加属性和方法; 最后返回被修改后的this。


  function Animal(name) {
    this.name = name
    this.canWalk = true
  }
  var animal = new Animal("beastie")
  alert(animal.name)

需要注意的是,如果构造函数显式返回一个对象,则this将指向该返回的对象。


  function Animal() {
    this.name = 'Mousie';
    this.age = '18';
    return {
      name: 'Godzilla'
    } // <-- will be returned
  }

  var animal = new Animal()
  console.log(animal.name) // Godzilla
  console.log(animal.age)//undefined

务必记得使用new关键字,否则不会创建新对象,而只是执行函数调用,此时this指向window。


function Vehicle(type, wheelsCount) { 
 this.type = type;
 this.wheelsCount = wheelsCount;
 return this;
}
// Function invocation
var car = Vehicle('Car', 4); 
car.type;    // => 'Car' 
car.wheelsCount // => 4 
car === window // => true

call与apply明确指定上下文

4. 明确指定this,使用call和apply

这是JavaScript最具特色的功能之一。

func.call(obj, arg1, arg2,...)

第一个参数作为this的指代对象,后续参数作为函数的参数。若需延后执行,可使用bind方法解决。


function Animal(type, legs) { 
 this.type = type;
 this.legs = legs; 
 this.logInfo = function() {
  console.log(this === myCat); // => true
  console.log('The ' + this.type + ' has ' + this.legs + ' legs');
 };
}
var myCat = new Animal('Cat', 4); 
// logs "The Cat has 4 legs"
setTimeout(myCat.logInfo.bind(myCat), 1000); 
// setTimeout??
 var john = {
  firstName: "John",
  surname: "Smith"
 }
 function func(a, b) {
  alert( this[a] + ' ' + this[b] )
 }
 func.call(john, 'firstName', 'surname') // "John Smith"

apply方法与call类似,区别在于参数以数组形式传入,其余部分相同。


 func.call(john, 'firstName', 'surname')
 func.apply(john, ['firstName', 'surname'])

它们也可用于ES5中的类继承,以调用父级构造器。


  function Runner(name) { 
   console.log(this instanceof Rabbit); // => true
   this.name = name; 
  }
  function Rabbit(name, countLegs) { 
   console.log(this instanceof Rabbit); // => true
   // 间接调用,调用了父级构造器
   Runner.call(this, name);
   this.countLegs = countLegs;
  }
  var myRabbit = new Rabbit('White Rabbit', 4); 
  myRabbit; // { name: 'White Rabbit', countLegs: 4 }

bind方法特性

5. .bind()方法

与.apply()和.call()立即执行函数不同,.bind()返回一个新函数,该函数绑定了预先指定的this,并可延后调用。

.bind()方法的作用是创建一个新的函数,其执行上下文环境为.bind()传递的第一个参数,从而允许创建预先设置好this的函数。


var numbers = { 
 array: [3, 5, 10],
 getNumbers: function() {
  return this.array;  
 }
};
// Create a bound function
var boundGetNumbers = numbers.getNumbers.bind(numbers); 
boundGetNumbers(); // => [3, 5, 10] 
// Extract method from object
var simpleGetNumbers = numbers.getNumbers; 
simpleGetNumbers(); // => undefined or throws an error in strict mode 

使用.bind()时需注意,它创建了一个永恒的上下文链且不可修改。即使后续使用.call()或.apply()传入不同的上下文,也不会改变之前绑定的上下文,重新绑定同样无效。

仅在构造器调用时,绑定函数可以改变上下文,但这并非推荐做法。

箭头函数的静态this

6. 箭头函数

箭头函数不创建自身执行的上下文,其this取决于定义时所在的外部函数。

箭头函数一旦绑定上下文便不可更改,即使使用上下文更改方法:


  var numbers = [1, 2]; 
  (function() { 
   var get = () => {
    console.log(this === numbers); // => true
    return this;
   };
   console.log(this === numbers); // => true
   get(); // => [1, 2]
   // 箭头函数使用 .apply() 和 .call()
   get.call([0]); // => [1, 2]
   get.apply([0]); // => [1, 2]
   // Bind
   get.bind([0])(); // => [1, 2]
  }).call(numbers);

这是因为箭头函数拥有静态的上下文环境,不会因不同的调用而改变。因此,不建议使用箭头函数定义对象方法。


function Period (hours, minutes) { 
   this.hours = hours;
   this.minutes = minutes;
  }
  Period.prototype.format = () => { 
   console.log(this === window); // => true
   return this.hours + ' hours and ' + this.minutes + ' minutes';
  };
  var walkPeriod = new Period(2, 30); 
  walkPeriod.format(); // => 'undefined hours and undefined minutes'

参考

Four scents of "this"

Gentle explanation of 'this' keyword in JavaScript

JavaScript This 之谜(译文)

强烈推荐觉得没弄明白的同学看看上面三篇文章,其中第三篇是第二篇的译文。如果大家对this还有疑问,也欢迎大家一起讨论,交流促进思考,共同进步。

作者最新文章