深入解析JavaScript中this的四种绑定规则

作者:函数茶馆

时间:2026-10-26

浏览:164

本文详细解析JavaScript中this的四种绑定规则:默认绑定、隐式绑定、显示绑定(call/apply/bind)以及new绑定。通过调用栈分析确定调用位置,结合箭头函数的词法作用特性,深入探讨this指向的动态变化,并解析常见前端面试题中的this指向逻辑。

文章目录

执行上下文

执行上下文主要分为两类:全局上下文和函数上下文。全局上下文仅存在一个,而函数上下文则在函数被调用时动态创建。

每个执行上下文都包含三个核心属性:

  • 变量对象
  • 作用域链
  • this

this到底是什么呢

this的绑定发生在运行时,而非代码编写阶段。其指向取决于函数被调用时的具体条件,与函数声明的位置无关,仅由调用方式决定。

调用位置

理解this绑定的关键在于确定“调用位置”,即函数实际被调用的位置,而非其声明位置。为此,我们需要分析调用栈(执行上下文栈)。

function baz() {
  console.log("baz")
  bar()
}
function bar() {
  console.log("bar")
  foo()
}
function foo() {
  console.log("foo")
}
baz()

当代码执行到foo()时,进入foo函数体,此时当前的调用栈为:

ECStack = [
    fooContext, // foo
    barContext, // bar
    bazContext, // baz
    globalContext, // 全局
]

通过调用栈可以清晰地定位函数的调用位置。例如,baz在全局环境中被调用,bar在baz内部被调用,而foo又在bar内部被调用。

那么,函数在执行时究竟如何确定this的绑定对象呢?

绑定规则

this的绑定对象由特定的绑定规则决定。

默认绑定

最常见的调用类型是独立函数调用。

function foo(){
    console.log(this.a) // 2
}

var a = 2;
foo()

当函数被调用时,若应用了默认绑定规则,this将指向全局对象。

如何判断应用了默认绑定?通过分析调用位置即可。如果foo()是直接使用不带任何修饰的函数引用进行调用,则只能应用默认绑定,无法应用其他规则。

因此,在全局环境中调用一个函数,函数内部的this指向全局变量window。

隐式绑定

function foo() {
    console.log( this.a );
}

var obj = {
    a: 2,
    foo: foo
};

obj.foo(); // 2

在上述代码中,foo的声明位置虽在全局,但它被作为引用属性添加到了obj对象中。调用位置使用obj上下文引用该函数。当foo被调用时,它被obj对象所包含,落脚点指向obj对象。当函数引用拥有上下文对象时,隐式绑定规则会将函数调用中的this绑定到这个上下文对象。

通过一个对象调用其内部的方法,该方法的执行上下文中的this指向对象本身。

我们看个特殊的例子

function foo() {
  console.log(this.a)
}

var obj = {
  a: 2,
  foo
}

var bar = obj.foo
var a = "mick"
bar() // mick

bar是obj.foo的一个引用,但它实际引用的是foo函数本身。因此,此时bar()是一个不带任何修饰的函数调用,应用了默认绑定规则。

我们再看另一种情况

function foo() {
  console.log(this.a)
}

function doFoo(fn) {
  fn()
}

var obj = {
  a: 2,
  foo
}

var a = "mick"

doFoo(obj.foo) // mick

嵌套函数中的this不会从外层函数中继承。this永远指向最后调用它的那个对象。

显示绑定

可以使用call、apply或bind方法。如果对这三个方法的实现原理感兴趣,可以参考相关手写实现文章。

function foo() {
  console.log(this.a)
}
var obj = {
  a: 2
}

foo.call(obj) // 2

通过call方法,可以在调用foo时,强制将它的this绑定到obj上。

new绑定

这里我们先说明使用new调用函数时会发生哪些事情:

  • 创建一个全新的对象
  • 这个新对象会被执行[[原型]]链接
  • 这个新对象会绑定到函数调用的this
  • 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。
function foo(a){
    this.a = a
}
var bar = new foo(2)
console.log(bar.a)

使用new来调用foo时,我们会构造一个新对象,并将其绑定到foo调用中的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()。所以这里应用的是默认绑定规则。

面试题

下面我们看个面试题吧

var name = 'window'
var person1 = {
  name: 'person1',
  foo1: function () {
    console.log(this.name)
  },
  foo2: () => console.log(this.name),
  foo3: function () {
    return function () {
      console.log(this.name)
    }
  },
  foo4: function () {
    return () => {
      console.log(this.name)
    }
  }
}
var person2 = { name: 'person2' }

person1.foo1()
person1.foo1.call(person2)

person1.foo2()
person1.foo2.call(person2)

person1.foo3()()
person1.foo3.call(person2)()
person1.foo3().call(person2)

person1.foo4()()
person1.foo4.call(person2)()
person1.foo4().call(person2)

我们一个个来解析一下。

  • person.foo1()这个属于隐式绑定,foo1的this绑定到了person,所以打印person1
  • person1.foo1.call(person2)显示绑定,foo1的this通过call改变了this,指向了person,所以打印person2
  • person.foo2()因为foo2是箭头函数,所以它的this指向是它上一层this的指向,也就是window
  • person1.foo2.call(person2)和上面一条同样的道理,也是window
  • person1.foo3()()内部返回了一个函数,其实是一个函数的引用,此时this应该指向window,所以打印window
  • person1.foo3.call(person2)()通过call只是改变了foo3的this指向,和返回的函数没有什么关系,所以this还是指向window,打印window
  • person1.foo3().call(person2)通过call改变了foo3内部返回函数的this指向,所以打印person2
  • person1.foo4()()返回的是一个箭头函数,箭头函数的this是它上一层函数内部this的指向,所以也就是foo4this的指向。由于foo4被person1包含并调用,所以this指向person1,打印person1
  • person1.foo4.call(person2)(),此时foo4内部的this通过call改变成了person2,所以打印person
  • person1.foo4().call(person2)这个和person1.foo4()是一样的道理,打印person1

简单的谈了谈this的绑定,欢迎留言你的问题,大家一起学习一起进步!!!

到此这篇关于JavaScript中this的绑定你知道几种?的文章就介绍到这了,更多相关JavaScript this绑定内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!

实用工具推荐

电子包浆

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

使用 ›
图片压缩

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

使用 ›
相关文章