JavaScript中this的四种绑定规则详解

作者:木棉

时间:2026-10-23

浏览:287

本文详细解析JavaScript中this的四种绑定规则:默认绑定、隐式绑定、显式绑定(call/apply/bind)以及new绑定。通过对比不同调用方式下的this指向,结合箭头函数的词法作用特性,帮助开发者准确掌握this的行为机制,避免常见的绑定丢失问题。

文章目录

在JavaScript中,理解this的含义是掌握语言核心机制的关键。

在深入探讨之前,需要纠正一个常见的误解:this并不指向函数自身,也不指向函数的词法作用域。

如果仅从this的英文字面解释去理解,很容易产生误导。

实际上,this是在函数被调用时才确定的绑定关系。这意味着this具体指向哪里,完全取决于函数的调用方式。简而言之,谁调用了这个函数,this就指向谁。

关于JavaScript中this的基本说明如下。

需要注意的是,ES6引入的箭头函数采用词法作用域来确定this,这与常规函数不同。

使用this的主要目的是使API设计更加简洁,并提高代码的可复用性。

重申一点:this既不指向函数自身,也不指向函数的词法作用域。

this的最终指向仅取决于函数的调用方式。

JavaScript中this的绑定规则优先级如下。

优先级的顺序为:new绑定 > 显示绑定 > 隐式绑定 > 默认绑定。

默认绑定规则适用于独立函数调用的场景。

当进行独立函数调用时,无论该调用是否处于调用栈中,this都会指向全局对象(在浏览器环境中即为window)。

然而,在严格模式下,不能将全局对象用于默认绑定。


var a = 2;
function foo(){
  console.log(this.a);
}
function bar(){
  var a = 5;
  foo();
}
bar(); // 2

隐式绑定规则适用于函数引用有上下文对象的情况。

当函数引用存在上下文对象时,隐式绑定规则会将函数调用中的this绑定到该上下文对象上。

在对象属性引用链中,只有最后一层在调用位置起作用。

使用隐式绑定的前提是:对象内部必须包含一个指向函数的属性,且该对象可通过此属性间接引用该函数。


function foo() {
  console.log( this.a );
}
var obj2 = {
  a: 42,
  foo: foo
};
var obj1 = {
  a: 2,
  obj2: obj2
};
obj1.obj2.foo(); // 42

隐式丢失是隐式绑定中常见的问题。


function foo() {
  console.log( this.a );
}
var obj = {
  a: 2,
  foo: foo
};
var bar = obj.foo; // 这里bar将引用foo函数本身,所以不带有函数对象的上下文
var a = "oops, global"; // a是全局对象的属性
bar(); // "oops, global"

这种情况通常出现在回调函数中,即参数传递时的隐式赋值场景。


function foo() {
  console.log( this.a );
}
function doFoo(fn) {
  // 参数传递时,相当于fn = obj.foo,就和上个例子一样了
  fn(); // <-- call-site!
}
var obj = {
  a: 2,
  foo: foo
};
var a = "oops, global"; // `a` also property on global object
doFoo( obj.foo ); // "oops, global"

显式绑定通过call()和apply()方法实现。

使用call()和apply()时,通过传入一个对象(若传入的是基本类型,会被封装函数转为对象——即装箱操作),将this绑定到该对象上。

硬绑定是显式绑定的一种典型应用形式。


function foo() {
  console.log( this.a );
}
var obj = {
  a: 2
};
var bar = function() {
  foo.call( obj );
};
bar(); // 2
setTimeout( bar, 100 ); // 2
// 硬绑定后bar无论怎么调用,都不会影响foo函数的this绑定
bar.call( window ); // 2

硬绑定的典型应用如以下包裹函数所示。


function foo(something) {
  console.log( this.a, something );
  return this.a + something;
}
var obj = {
  a: 2
};
var bar = function() {
  return foo.apply( obj, arguments ); // 将obj对象硬编码进去
};
var b = bar( 3 ); // 2 3
console.log( b ); // 5

即将内部函数用apply硬绑定到某个对象,无论怎么调用这个包裹函数,都不会影响内部函数的this。

bind辅助函数的实现如下。


function foo(something) {
  console.log( this.a, something );
  return this.a + something;
}
// simple `bind` helper
function bind(fn, obj) {
  return function() {
    return fn.apply( obj, arguments ); // 利用参数将obj传入进去
  };
}
var obj = {
  a: 2
};
var bar = bind( foo, obj ); // bind( foo, obj )会返回一个包裹函数
var b = bar( 3 ); // 2 3
console.log( b ); // 5

总结:上述包裹函数,想要包裹其他函数,只能一个一个重复写,硬编码的方式导致不能被重用,当某种功能需要多次重复使用时,将其抽象出来,成为函数。

new绑定规则适用于构造函数调用场景。

任何函数都可能被用作构造函数,当函数被new操作符“构造调用”时,会执行下面操作。

1. 创建一个新对象(若该函数不是JS内置的,则创建一个新的Object对象)。

2. 将this绑定到这个新创建的对象。

3. 执行构造函数中的代码(为这个新对象添加属性)。

4. 若函数没有返回其他对象,则自动返回这个新对象;若函数有return返回的是非对象,则还是自动返回这个新对象,即覆盖那个非对象。


function foo(a) {
  this.a = a;
}
var bar = new foo( 2 );
console.log( bar.a ); // 2

补充说明部分涉及间接引用和箭头函数的特殊情况。

间接引用是指通过某种方式间接访问函数,可能影响this的绑定。


function foo() {
  console.log( this.a );
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
(p.foo = o.foo)(); // 2,由于p.foo = o.foo的返回值是目标函数的引用,所以调用位置是foo(),而不是p.foo()或o.foo()

箭头函数:不使用这四个this规则,根据词法作用域来决定this。


function foo() {
  // 返回一个箭头函数
  return (a) => {
    // `this` here is lexically adopted from `foo()`
    console.log( this.a );
  };
}
var obj1 = {
  a: 2
};
var obj2 = {
  a: 3
};
// foo()不是箭头函数,他的this被绑定到obj1
var bar = foo.call( obj1 ); // foo.call( obj1 )返回箭头函数,所以bar为箭头函数
bar.call( obj2 ); // 2! 箭头函数的this无法被修改,new也不行

如下为和箭头函数一样的模式:


function foo() {
  var self = this; // lexical capture of `this`
  setTimeout( function(){
    console.log( self.a );
  }, 100 );
}
var obj = {
  a: 2
};

更多关于JS中this对象的相关文章大家可以点击下面的相关链接

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章