深入理解 JavaScript 函数调用中的 this 指向与规则

作者:纸飞机时间:2026-10-25 07:43:01浏览:415

本文深入解析 JavaScript 中函数调用时 this 值的确定机制,详细阐述直接调用、方法调用、构造函数调用及隐式绑定四种模式。通过具体代码示例,揭示事件处理中 this 丢失的常见陷阱,并提供避免 Bug 的最佳实践,帮助开发者准确掌握对象与方法调用的核心逻辑。

JavaScript 函数调用机制概述

透彻理解 JavaScript 函数的工作机制,特别是调用方式,能有效预防许多潜在的代码错误。


首先定义一个辅助函数,供后续示例使用。该函数仅返回当前的 this 值以及传入的两个参数。


代码实现如下:

 
function makeArray(arg1, arg2){
return [ this, arg1, arg2 ];
} 

调用该函数十分直观,只需执行以下操作:



代码示例:

 
makeArray('one', 'two');

返回结果:=> [ window, 'one', 'two' ]


这里出现了一个疑问:为什么 this 的值会指向 window?下面进行简要分析。


在 JavaScript 中,存在一个全局对象。看似分散在脚本各处的代码,实际上都运行在这个全局对象的上下文中。在之前的例子中,makeArray 并非一个孤立的全局函数,而是全局对象的一个方法。以浏览器环境为例,其全局对象映射为 window 对象。我们可以通过以下方式验证:


代码示例:

 
alert( typeof window.makeArray);

返回结果:=> function



这意味着,之前直接调用 makeArray 的方式,与通过 window 对象调用是等价的。


代码示例:

 
window.makeArray('one', 'two');

返回结果:=> [ window, 'one', 'two' ]



直接调用模式:this 指向全局对象

JavaScript 函数调用规则 1:当函数被直接调用(如 myFunction())且未明确指定所有者对象时,this 的值默认为全局对象(在浏览器环境中即为 window)。



接下来,创建一个简单对象,并将 makeArray 函数作为其方法。我们使用 JSON 格式声明该对象,并调用该方法:



代码示例:

 
var arrayMaker = {
someProperty: 'some value here',
make: makeArray
}; 
arrayMaker.make('one', 'two');
// 返回:=> [ arrayMaker, 'one', 'two' ]
arrayMaker['make']('one', 'two');
// 返回:=> [ arrayMaker, 'one', 'two' ]

此时,this 的值变为对象 arrayMaker 本身。你可能会疑惑,原始函数定义并未改变,为何 this 不是 window?这是因为函数也是对象,可以传递或复制。这相当于将整个函数(包括参数列表和函数体)复制并赋值给 arrayMaker 的 make 属性,其效果等同于如下定义:


代码示例:

 
var arrayMaker = {
someProperty: 'some value here',
make: function (arg1, arg2) {return [ this, arg1, arg2 ];}
}; 

方法调用模式:this 指向调用对象

JavaScript 函数调用规则 2:当使用方法调用语法(如 obj.myFunction() 或 obj['myFunction']())时,this 的值指向该对象 obj。



事件处理中的 this 陷阱

这是事件处理代码中 Bug 的主要来源之一。请看以下示例:



代码示例:

 
<input type="button" value="Button 1" id="btn1" />
<input type="button" value="Button 2" id="btn2" />
<input type="button" value="Button 3" id="btn3" onclick="buttonClicked();"/>
<script type="text/javascript">
function buttonClicked(){
var text = (this === window) ? 'window' : this.id;
alert( text );
} 
var button1 = document.getElementById('btn1');
var button2 = document.getElementById('btn2');
button1.onclick = buttonClicked;
// 返回结果:=> btn1,这是一个方法调用,this 指向所属对象(按钮元素)。
button2.onclick = function(){buttonClicked();};
// 返回结果:=> window,因为 buttonClicked() 是被直接调用的(不同于 obj.buttonClicked())。这与第三个按钮的行为一致,即将事件处理函数直接写在标签内。因此,点击第三个按钮的结果与第二个相同,均为 window。
</script>

我们知道 JavaScript 中没有传统的类,自定义类型需要一个初始化函数。通过原型对象(作为初始化函数的属性)来定义类型。让我们创建一个简单的类型:



代码示例:

 
function ArrayMaker(arg1, arg2) {
this.someProperty = 'whatever';
this.theArray = [ this, arg1, arg2 ];
} 
// 声明实例化方法
ArrayMaker.prototype = {
someMethod: function () {
alert( 'someMethod called');
}, 
getArray: function () {
return this.theArray;
} 
}; 
var am = new ArrayMaker( 'one', 'two' );
var other = new ArrayMaker( 'first', 'second' );
am.getArray();
// 返回结果:=> [ am, 'one' , 'two' ]

构造函数调用模式:new 运算符的作用

值得注意的是 new 运算符。若不使用 new,函数就像全局函数一样执行,且创建的属性将挂载到全局对象(window)上,这通常不是我们想要的。另外,由于构造函数没有返回值,若忘记使用 new,变量将被赋值为 undefined。因此,构造函数以大写字母开头是一个良好的习惯,以此提醒调用者必须使用 new 运算符。



JavaScript 函数调用规则 3:当将函数用作初始化函数(如 new MyFunction())时,JavaScript 运行时会将 this 的值指定为新建的对象。

作者最新文章