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

作者:闭包旅人

时间:2026-10-22

浏览:268

本文详细解析JavaScript函数中this指向的四种绑定形式:默认绑定指向全局对象,隐式绑定指向调用对象但易发生丢失,显式绑定通过call/apply/bind强制指定,以及new绑定指向新实例。文章结合代码示例与通俗比喻,帮助开发者彻底厘清this的动态绑定机制。

文章目录

正文

JavaScript中的this与函数紧密相关,本文将详细阐述JavaScript函数中this的具体指向问题。

提到this,常伴随令人困惑的抽象概念。这里只聚焦核心原则:函数中的this始终指向调用它的对象,后续内容均基于此展开。

(提醒前排的筒子们准备好茶水和西瓜,我要开始讲故事啦!!)

【故事】有一个年轻人叫"迪斯"(this),有一天,迪斯不小心穿越到一个叫 “伽瓦斯克利”(javascript)的 异世界,此时此刻迪斯身无分文, 他首先要做的事情就是——找到他的住宿的地方——调用函数的对象

默认绑定:指向全局对象

默认绑定:指向全局对象

this的默认绑定

【故事——线路1】如果迪斯(this)直到天黑前都没有找到能收留自己的住所,他眼看就要过上非洲难民的生活, 这时候,一位乐善好施的魔法师村长——window救世主一般地出现了:先住在我家吧!

默认绑定:指向全局对象

【正文】

当一个函数没有明确的调用对象,即作为独立函数被调用时,this采用默认绑定,指向全局window对象。


function fire () {
  console.log(this === window)
}
fire(); // 输出true

上述例子对大多数人来说很简单,但稍作变化便会产生迷惑性:


function fire () {
 // 我是被定义在函数内部的函数哦!
  function innerFire() {
 console.log(this === window)
  }
  innerFire(); // 独立函数调用
}
fire(); // 输出true

函数innerFire在外部函数fire内部声明并调用,其this指向谁?答案仍是window。

许多人可能担心fire函数的作用域会影响innerFire,但只要抓住理论核心——没有明确调用对象时,this使用默认绑定指向全局window对象,即可得出正确答案。

下面这个加强版的例子输出结果同样为true。


var obj = {
 fire: function () {
  function innerFire() {
   console.log(this === window)
  }
  innerFire(); // 独立函数调用
  }
}
obj.fire(); //输出 true

【注意】在这个例子中,obj.fire()的调用实际上使用到了this的隐式绑定,这就是下面我要讲的内容,这个例子我接下来还会继续讲解

【总结】 凡事函数作为独立函数调用,无论它的位置在哪里,它的行为表现,都和直接在全局环境中调用无异

隐式绑定与this丢失

this的隐式绑定

【故事——线路2】 迪斯(this)穿越来异世界“伽瓦斯克利”(javascript)的时候,刚好身上带了一些钱,于是他找到一个旅馆住宿了下来

默认绑定:指向全局对象

当函数被一个对象“包含”时,我们称函数的this被隐式绑定到该对象。此时,通过this可直接访问所绑定对象内的其他属性,例如下面的a属性。


var obj = {
  a: 1,
  fire: function () {
   console.log(this.a)
  }
}
obj.fire(); // 输出1 

现在我们需要对平常司空见惯的的代码操作做一些更深的思考,首先,下面的这两段代码达到的效果是相同的:


// 我是第一段代码
function fire () {
  console.log(this.a)
}
var obj = {
  a: 1,
  fire: fire
 }
obj.fire(); // 输出1
// 我是第二段代码
var obj = {
  a: 1,
  fire: function () {
    console.log(this.a)
   }
}
obj.fire(); // 输出1

fire函数并不会因为它被定义在obj对象的内部和外部而有任何区别,也就是说在上述隐式绑定的两种形式下,fire通过this还是可以访问到obj内的a属性,这告诉我们:

1. this是动态绑定的,或者说是在代码运行期绑定而不是在书写期

2. 函数于对象的独立性, this的传递丢失问题

(下面的描述可能带有个人的情感倾向而显得不太严谨,但这是因为我希望阅读者尽可能地理解我想表达的意思) 隐式绑定下,作为对象属性的函数,对于对象来说是独立的

基于this动态绑定的特点,写在对象内部、作为对象属性的函数,对于这个对象来说是独立的。(函数并不被这个外部对象所“完全拥有”)

我想表达的意思是:在上文中,函数虽然被定义在对象的内部中,但它和“在对象外部声明函数,然后在对象内部通过属性名称的方式取得函数的引用”,这两种方式在性质上是等价的(而不仅仅是效果上)

定义在对象内部的函数只是“恰好可以被这个对象调用”而已,而不是“生来就是为这个对象所调用的”

借用下面的隐式绑定中的this传递丢失问题来说明:


var obj = {
  a: 1, // a是定义在对象obj中的属性 1
  fire: function () {
 console.log(this.a)
  }
  }
var a = 2; // a是定义在全局环境中的变量 2
var fireInGrobal = obj.fire; 
fireInGrobal(); // 输出 2

上面这段简单代码的有趣之处在于: 这个于obj中的fire函数的引用( fireInGrobal)在调用的时候,行为表现(输出)完全看不出来它就是在obj内部定义的,其原因在于:我们隐式绑定的this丢失了!! 从而 fireInGrobal调用的时候取得的this不是obj,而是window

上面的例子稍微变个形式就会变成一个可能困扰我们的bug:

在上面,我们的


var a = 2;
var obj = {
 a: 1, // a是定义在对象obj中的属性
 fire: function () {
   console.log(this.a)
  }
} 
function otherFire (fn) {
  fn();
} 
otherFire(obj.fire); // 输出2

关键角色是otherFire函数,它接受一个函数引用作为参数,然后在内部直接调用,但它做的假设是参数fn仍然能够通过this去取得obj内部的a属性,但实际上, this对obj的绑定早已经丢失了,所以输出的是全局的a的值(2),而不是obj内部的a的值(1)

在一串对象属性链中,this绑定的是最内层的对象

在隐式绑定中,如果函数调用位置是在一串对象属性链中,this绑定的是最内层的对象。如下所示:


var obj = {
  a: 1,
  obj2: {
   a: 2,
   obj3: {
    a:3,
    getA: function () {
     console.log(this.a) 
     }
   }
  }
}
obj.obj2.obj3.getA(); // 输出3

显式绑定:call与bind

this的显式绑定:(call和bind方法)

【故事——线路3】 迪斯(this)穿越来异世界“伽瓦斯克利”(javascript),经过努力的打拼,积累了一定的财富,于是他买下了自己的房子

默认绑定:指向全局对象

上面我们提到了this的隐式绑定所存在的this绑定丢失的问题,也就是对于 “ fireInGrobal = obj.fire”

fireInGrobal调用和obj.fire调用的结果是不同的,因为这个函数赋值的过程无法把fire所绑定的this也传递过去。这个时候,call函数就派上用场了

call的基本使用方式: fn.call(object)

fn是你调用的函数,object参数是你希望函数的this所绑定的对象。

fn.call(object)的作用:

1.即刻调用这个函数(fn)

2.调用这个函数的时候函数的this指向object对象

例子:


var obj = {
  a: 1, // a是定义在对象obj中的属性
  fire: function () {
   console.log(this.a)
  }
}
var a = 2; // a是定义在全局环境中的变量 
var fireInGrobal = obj.fire;
fireInGrobal(); // 输出2
fireInGrobal.call(obj); // 输出1

原本丢失了与obj绑定的this参数的fireInGrobal再次重新把this绑回到了obj

但是,我们其实不太喜欢这种每次调用都要依赖call的方式,我们更希望:能够一次性 返回一个this被永久绑定到obj的fireInGrobal函数,这样我们就不必每次调用fireInGrobal都要在尾巴上加上call那么麻烦了。

怎么办呢? 聪明的你一定能想到,在fireInGrobal.call(obj)外面包装一个函数不就可以了嘛!


var obj = {
  a: 1, // a是定义在对象obj中的属性
  fire: function () {
  console.log(this.a)
  }
}
var a = 2; // a是定义在全局环境中的变量 
var fn = obj.fire;
var fireInGrobal = function () {
 fn.call(obj) //硬绑定
}
fireInGrobal(); // 输出1 

如果使用bind的话会更加简单


var fireInGrobal = function () {
 fn.call(obj) //硬绑定
}

可以简化为:


var fireInGrobal = fn.bind(obj);

call和bind的区别是:在绑定this到对象参数的同时:

1.call将立即执行该函数

2.bind不执行函数,只返回一个可供执行的函数

【其他】:至于apply,因为除了使用方法,它和call并没有太大差别,这里不加赘述

在这里,我把显式绑定和隐式绑定下,函数和“包含”函数的对象间的关系比作买房和租房的区别。

默认绑定:指向全局对象

因为this的缘故

在隐式绑定下:函数和只是暂时住在“包含对象“的旅馆里面,可能过几天就又到另一家旅馆住了

在显式绑定下:函数将取得在“包含对象“里的永久居住权,一直都会”住在这里“

new绑定:构造函数中的this

new绑定

【故事】 迪斯(this)组建了自己的家庭,并生下多个孩子(通过构造函数new了许多个对象)

默认绑定:指向全局对象

执行new操作的时候,将创建一个新的对象,并且将构造函数的this指向所创建的新对象


function foo (a) {
  this.a = a;
}
var a1 = new foo (1);
var a2 = new foo (2);
var a3 = new foo (3);
var a4 = new foo (4);
console.log(a1.a); // 输出1
console.log(a2.a); // 输出2
console.log(a3.a); // 输出3
console.log(a4.a); // 输出4

总结

以上所述是小编给大家介绍的JavaScript函数中的this四种绑定形式,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对本站网站的支持!

实用工具推荐

电子包浆

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

使用 ›
图片压缩

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

使用 ›
相关文章