图解JavaScript中的this关键字

作者:青柠

时间:2026-10-29

浏览:523

本文深入解析JavaScript中this关键字的绑定机制。通过决策树图解,详细阐述作为对象方法、普通函数、构造函数以及使用apply/call调用时this的不同指向。结合具体代码案例,说明非严格模式下this指向全局对象或当前对象的判断逻辑,帮助开发者掌握这一核心概念。

文章目录

JavaScript this 关键字概述

JavaScript 是一门支持函数式编程、闭包及基于原型继承等高级特性的脚本语言。尽管其入门门槛看似较低,但随着开发深入,许多基础概念显得晦涩难懂。其中,this 关键字因在不同场景下指向不同对象而常令人困惑。业界有一种观点认为,只有透彻理解 this 的机制,才算真正跨入 JavaScript 开发的门槛。与 Java、C# 等主流面向对象语言在编译期绑定 this 指向当前对象不同,JavaScript 中的 this 是在运行期动态绑定的,这正是其含义多变且复杂的根本原因。

this 指向与函数调用方式

得益于运行期绑定的特性,JavaScript 中的 this 可能指向全局对象、当前对象或任意其他对象,具体取决于函数的调用方式。JavaScript 主要包含四种函数调用模式:作为对象方法调用、作为普通函数调用、作为构造函数调用,以及通过 apply 或 call 方法调用。俗话说“一图胜千言”,为了更直观地展示 this 的指向逻辑,下文将通过一张决策图进行解析。

 JavaScript this 关键字概述

上图被称为“JavaScript this 决策树”(适用于非严格模式)。下面通过具体示例演示如何利用该决策树判断 this 的指向。


var point = { 
 x : 0, 
 y : 0, 
 moveTo : function(x, y) { 
 this.x = this.x + x; 
 this.y = this.y + y; 
 } 
 };
 //决策树解释:point.moveTo(1,1)函数不是new进行调用,进入否决策,
 //是用dot(.)进行调用,则指向.moveTo之前的调用对象,即point
 point.moveTo(1,1); //this 绑定到当前对象,即point对象

对象方法调用中的 this 指向

以 point.moveTo() 函数为例,其在“JavaScript this 决策树”中的判定路径如下:

1)判断 point.moveTo 函数是否通过 new 关键字调用?显然不是,因此进入“否”分支,继续判断函数是否通过点运算符(.)调用;

2)point.moveTo 函数确实是通过点运算符(.)调用的,因此进入“是”分支,此时 this 指向点运算符左侧的对象,即 point;

point.moveTo 函数 this 指向的图解分析如下图所示:

JavaScript this 关键字概述 

再举一个代码示例:


function func(x) { 
 this.x = x; 
 } 
func(5); //this是全局对象window,x为全局变量
 //决策树解析:func()函数是用new进行调用的么?为否,进入func()函数是用dot进行调用的么?为否,则 this指向全局对象window
 x;//x => 5

普通函数调用中的 this 指向

func() 函数在“JavaScript this 决策树”中的判定过程如下:

1)判断 func(5) 是否通过 new 关键字调用?显然不是,进入“否”分支,继续判断函数是否通过点运算符(.)调用;

2)func(5) 并非通过点运算符调用,因此进入“否”分支,此时 this 指向全局变量 window,故 this.x 实际上等同于 window.x;

func 函数 this 指向的图解分析如下图所示:

 JavaScript this 关键字概述

针对作为普通函数直接调用的情况,下面看一个更复杂的例子:


var point = { 
 x : 0, 
 y : 0, 
 moveTo : function(x, y) { 
 // 内部函数
 var moveX = function(x) { 
 this.x = x;//this 指向什么?window
 }; 
 // 内部函数
 var moveY = function(y) { 
 this.y = y;//this 指向什么?window
 }; 
 moveX(x); 
 moveY(y); 
 } 
 }; 
 point.moveTo(1,1); 
 point.x; //=>0 
 point.y; //=>0 
 x; //=>1 
 y; //=>1

内部函数中的 this 指向

point.moveTo(1,1) 函数内部实际调用了 moveX() 和 moveY() 函数。对于 moveX() 函数内部的 this,其在“JavaScript this 决策树”中的判定过程如下:

1)判断 moveX(1) 是否通过 new 关键字调用?显然不是,进入“否”分支,继续判断函数是否通过点运算符(.)调用;

2)moveX(1) 并非通过点运算符调用,因此进入“否”分支,此时 this 指向全局变量 window,故 this.x 实际上等同于 window.x;

接下来看作为构造函数调用的示例:


 function Point(x,y){ 
 this.x = x; // this ?
 this.y = y; // this ?
 }
 var np=new Point(1,1);
 np.x;//1
 var p=Point(2,2);
 p.x;//error, p是一个空对象undefined
 window.x;//2

构造函数调用中的 this 指向

在语句 var np=new Point(1,1) 中,Point(1,1) 函数的 this 在“JavaScript this 决策树”中的判定过程如下:

1)判断 var np=new Point(1,1) 是否通过 new 关键字调用?显然是,因此进入“是”分支,此时 this 指向新创建的实例 np;

2)因此 this.x=1 等价于 np.x=1;

再看 var p= Point(2,2) 中 Point(2,2) 函数的 this 在“JavaScript this 决策树”中的判定过程:

1)判断 var p= Point(2,2) 是否通过 new 关键字调用?显然不是,进入“否”分支,继续判断函数是否通过点运算符(.)调用;

2)Point(2,2) 并非通过点运算符调用,判定为否,进入“否”分支,此时 this 指向全局变量 window,故 this.x 实际上等同于 window.x;

3)执行 this.x=2 即 window.x=2。

最后,查看通过 call 和 apply 方法调用的示例:


 function Point(x, y){ 
 this.x = x; 
 this.y = y; 
 this.moveTo = function(x, y){ 
 this.x = x; 
  this.y = y; 
 } 
 } 
 
 var p1 = new Point(0, 0); 
 var p2 = {x: 0, y: 0}; 
 p1.moveTo.apply(p2, [10, 10]);//apply实际上为p2.moveTo(10,10)
 p2.x//10

apply 和 call 调用中的 this 指向

p1.moveTo.apply(p2,[10,10]) 函数在“JavaScript this 决策树”中的判定过程如下:

apply 和 call 方法功能强大,允许切换函数执行的上下文环境(context),即改变 this 绑定的对象。p1.moveTo.apply(p2,[10,10]) 实际上等价于 p2.moveTo(10,10)。因此,p2.moveTo(10,10) 可解释为:

1)判断 p2.moveTo(10,10) 是否通过 new 关键字调用?显然不是,进入“否”分支,继续判断函数是否通过点运算符(.)调用;

2)p2.moveTo(10,10) 是通过点运算符调用的,因此进入“是”分支,此时 this 指向点运算符左侧的对象 p2,故 p2.x=10;

函数执行环境解析

关于 JavaScript 函数执行环境的过程,IBM developerworks 文档库中的一段描述非常精辟,摘录如下:

“JavaScript 中的函数既可以被当作普通函数执行,也可以作为对象的方法执行,这是导致 this 含义如此丰富的主要原因。一个函数被执行时,会创建一个执行环境(ExecutionContext),函数的所有的行为均发生在此执行环境中,构建该执行环境时,JavaScript 首先会创建 arguments 变量,其中包含调用函数时传入的参数。接下来创建作用域链。然后初始化变量,首先初始化函数的形参表,值为 arguments 变量中对应的值,如果 arguments 变量中没有对应值,则该形参初始化为 undefined。如果该函数中含有内部函数,则初始化这些内部函数。如果没有,继续初始化该函数内定义的局部变量,需要注意的是此时这些变量初始化为 undefined,其赋值操作在执行环境(ExecutionContext)创建成功后,函数执行时才会执行,这点对于我们理解 JavaScript 中的变量作用域非常重要,鉴于篇幅,我们先不在这里讨论这个话题。最后为 this 变量赋值,如前所述,会根据函数调用方式的不同,赋给 this 全局对象,当前对象等。至此函数的执行环境(ExecutionContext)创建成功,函数开始逐行执行,所需变量均从之前构建好的执行环境(ExecutionContext)中读取。”

深入理解这段描述对于掌握 JavaScript 函数机制大有裨益。

以上是对 JavaScript 中 this 关键字的详细介绍。通常图片比文字更易于理解,希望本文能对大家的学习有所帮助。

实用工具推荐

电子包浆

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

使用 ›
图片压缩

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

使用 ›
相关文章