JavaScript中this关键字指向规则详解

作者:微光

时间:2026-10-30

浏览:151

本文详细解析JavaScript中this关键字的指向规则。通过全局函数、对象方法、事件处理函数以及call/apply/bind等场景,说明this在不同调用方式下的具体指向。重点讲解事件绑定中赋值与引用的区别,以及如何通过改变调用上下文来修正this指向,帮助开发者避免常见的this绑定错误。

文章目录

this 关键字指向原则

与其他面向对象语言类似,JavaScript 也提供了 this 关键字,用于在函数内部指向调用该方法的对象。在实际开发中,确定 this 的具体指向至关重要,通常可以遵循以下核心原则进行判断:

  • 当函数通过 Function.call 或 Function.apply 方法被调用时,this 指向这两个方法的第一个参数。若该参数为 null 或 undefined,则 this 会指向全局对象(在浏览器环境中即 window 对象)。
  • 若函数通过 Function.bind 方法被调用,this 将指向 bind 方法第一个参数所指定的对象(该绑定在方法创建时即已确定)。
  • 当函数作为某个对象的方法被调用时,this 指向该对象本身。
  • 如果函数以独立函数的形式被调用,且不依附于任何特定对象,则 this 指向全局变量 window。

全局函数中的 this 指向

全局函数场景

首先,我们来分析“全局函数”的情况:


function introduce() {
   alert("Hello, I am Laruence\r\n");
}

对于上述定义的函数,this 关键字究竟指向哪里呢?

定义在全局作用域内的函数,其所有者即为当前页面,也就是 window 对象。

这也是为什么我将函数名用引号括起来的原因。因为定义在全局的函数,本质上就是 window 对象的一个方法。

因此,我们既可以直接通过函数名调用,也可以通过 window.方法名的方式调用。此时,方法内部的 this 关键字指向其所有者:window 对象。

如果我们查看 window 对象的 introduce 属性,会得到如下结果:


var name = "I am Laruence";
function introduce() {
   alert(this.name);
}
alert(window.introduce);
/**
* output:
* function introduce() {
* alert(this.name);
* }
*/

阅读上述代码后,你可能会联想到:既然全局函数是 window 对象的方法,而全局变量是 window 对象的属性(前文作用域部分已提及),那么在全局函数中是否可以通过 this 关键字来访问全局变量呢?

答案是肯定的。如果调用 introduce 函数,你将看到输出:“我是 Laruence”。

事件处理函数中的 this 指向

事件处理函数场景

或许,导致 this 关键字令人困惑的主要原因,在于将函数(方法)应用于事件处理时。


<input id="name" type="text" name="name" value="Laruence" />

例如,现在我们需要在点击“name”输入框时,显示该输入框的 value 值。此时可以编写如下代码:


function showValue() {
   alert(this.value);
}
document.getElementById('name').onclick = showValue;

上述代码运行正常,但为何如此?不是说函数的 this 指针永远指向函数所有者吗?不是说全局变量的所有者是 window 对象吗?

如果你能想到这个问题,说明你正在认真研读本文;否则,建议你从头开始阅读,否则即便读完,你可能依然感到困惑。

没错,对于上述代码,showValue 确实定义在全局对象中,那么问题只能出在 onclick 事件绑定的环节。

我们知道,在 JavaScript 中一切皆对象,函数和方法也是对象的属性,区别仅在于函数具有可执行的内部属性。因此,在上述代码中,对 onclick 绑定处理器时,实际上是将 id 为 name 的输入框 DOM 对象的 onclick 属性进行了赋值。

也就是说,我们将 showValue 函数复制给了 name 输入框对象的 onclick 属性。如果此时查看该 onclick 属性,会发现:


function showValue() {
   alert(this.value);
}
document.getElementById('name').onclick = showValue;
alert(document.getElementById('name').onclick);
/**
* output
* function showValue() {
* alert(this.value);
* }
*/

因此,当事件触发时,便会调用 name 输入框的 onclick 方法,此时 this 关键字自然指向 name 输入框元素。

然而,令人困惑的情况随之而来,例如下面这种写法:


function showValue() {
   alert(this.value);
}
<input id="name" type="text" name="name" value="Laruence" onclick="showValue()"/>

该写法无法正常运行,这又是为什么呢?

这是因为此时并非赋值操作,而是引用传递。

如果我们注意两种 onclick 的写法,会发现之前的方法使用的是:


dom.onclick = showvalue; //没有调用符

而刚才的方法使用的是:


onclick = "showvalue()" //有调用符

这也能侧面反映出两者的区别:前者是赋值,后者是引用。如果此时查看输入框的 onclick 属性,我们会得到:


alert(dom.onclick);
/**
* output:
* function onclick() {
*  showValue();
* }
*/

看到区别了吗?这就解释了原因所在。

讲到这里,有一个有趣的例子,大家可以在 IE 浏览器下尝试:


<img src="xxx" onerror="alert(1);} function hi() { alert(2); " />

改变 this 指向的方法

改变 this 指向的方法

既然我们已经知道了原因,那么如何让 this 指向我们期望的目标呢?

对于上述事件处理函数,我们可以采用以下几种写法:


dom.onclick = showValue();
dom.onclick = function() { alert(this.value) ;}
<input onclick="alert(this.value);" /> //想想刚才我们的引用,是如何把这句嵌入的.
dom.addEventListener(dom, showValue, false); //ff only

而对于非事件处理函数的场合,我们可以使用 apply 或 call 方法来改变 this 关键字的指向。

例如:


var laruence = {
   name : 'laruence',
   age : 26,
   position : 'Senior PHP Engineer',
   company : 'Baidu.inc'
};
 
function introduce() {
   alert(this.name);
}
 
introduce.call(laruence);

函数被 Function.call 调用的示例:


var myObject = {
  sayHello : function() {
    console.log('Hi! My name is ' + this.myName);
  },

  myName : 'Rebecca'
};

var secondObject = {
  myName : 'Colin'
};

myObject.sayHello();         // logs 'Hi! My name is Rebecca'
myObject.sayHello.call(secondObject); // logs 'Hi! My name is Colin'

函数被 Function.call 调用后的结果:


var myName = 'the global object',

  sayHello = function () {
    console.log('Hi! My name is ' + this.myName);
  },

  myObject = {
    myName : 'Rebecca'
  };

var myObjectHello = sayHello.bind(myObject);

sayHello();    // logs 'Hi! My name is the global object'
myObjectHello(); // logs 'Hi! My name is Rebecca'

函数被对象调用的示例:


var myName = 'the global object',

  sayHello = function() {
    console.log('Hi! My name is ' + this.myName);
  },

  myObject = {
    myName : 'Rebecca'
  },

  secondObject = {
    myName : 'Colin'
  };

myObject.sayHello = sayHello;
secondObject.sayHello = sayHello;

sayHello();        // logs 'Hi! My name is the global object'
myObject.sayHello();   // logs 'Hi! My name is Rebecca'
secondObject.sayHello(); // logs 'Hi! My name is Colin'

在调用深层命名空间中的函数时,我们通常会缓存一个变量指向该函数,以减少代码量。但这样做会改变函数内部 this 的值,导致执行错误的操作。例如:


var myNamespace = {
  myObject : {
    sayHello : function() {
      console.log('Hi! My name is ' + this.myName);
    },

    myName : 'Rebecca'
  }
};

var hello = myNamespace.myObject.sayHello;

hello(); // logs 'Hi! My name is undefined'

因此,若要缓存变量以节省代码量,正确的做法是只缓存到调用该函数的对象为止:


var myNamespace = {
  myObject : {
    sayHello : function() {
      console.log('Hi! My name is ' + this.myName);
    },

    myName : 'Rebecca'
  }
};

var obj = myNamespace.myObject;

obj.sayHello(); // logs 'Hi! My name is Rebecca'

总之,有一个核心原则:谁调用了那个函数,this 就指向谁。

实用工具推荐

电子包浆

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

使用 ›
图片压缩

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

使用 ›
相关文章