JavaScript中this关键字的指向规则与场景解析

作者:木棉

时间:2026-10-29

浏览:606

本文详细解析JavaScript中this关键字在不同执行环境下的指向逻辑。涵盖全局上下文、对象方法调用、构造函数、自执行函数、DOM事件绑定及原型方法等场景,通过具体代码示例说明this动态绑定的机制与常见陷阱,帮助开发者准确掌握this的使用规则。

文章目录

JavaScript this关键字概述

this关键字在C++、Java等语言中早已存在,初学者常觉晦涩,但一旦掌握其核心逻辑,便能大幅提升编码效率。本文将系统梳理JavaScript中this关键字的用法与原理。

this是前端面试的高频考点,许多开发者在实际编码中也会因this指向问题遭遇困惑。例如在使用Knockout等框架时,常需显式传递this而非直接依赖当前上下文,理解其机制至关重要。

以下内容基于个人实践总结,旨在作为技术笔记供后续查阅。如有疏漏或错误,欢迎指正与讨论。

this的动态指向与基本规则

与C#等语言中this固定指向当前实例不同,JavaScript的this具有动态性。

JavaScript中this的指向并非一成不变,而是随调用方式动态确定。call和apply方法正是利用这一特性来显式改变this指向,从而提升代码的灵活性与复用能力。

通常情况下,this指向定义该函数的对象所有者。

这一规则是理解JavaScript this机制的核心,务必重点掌握。

以下代码是常见的面试题示例:


<script type="text/javascript">
  var number = 1;
  var obj = {
     number: 2,
    showNumber: function(){
      this.number = 3;
      (function(){          
        console.log(this.number);
      })();
      console.log(this.number);
    }
  };
  obj.showNumber();
</script>

由于showNumber方法属于obj对象,因此方法内部的this指向obj。执行this.number = 3后,obj的number属性值变为3,此时this指向obj对象的number属性。

同理,第二个console.log输出的也是obj对象的number属性值。

为何前文称“一般情况下”this指向函数拥有者?因为存在特例。在函数自执行(IIFE)场景中,this默认指向全局对象window。因此,第一个console.log输出的是window对象的number属性值(即1)。

因此,需补充以下特例说明:

在函数自执行(IIFE)中,this指向全局window对象。

DOM事件中的this指向

此外,DOM事件处理中的this指向也常令人困惑,主要涉及以下三种注册方式:

具体示例如下:

1. 通过HTML标签属性注册事件时,this指向window对象。


<input id="test" type="button" value="按钮" onClick="test()"/>
  function test(){alert(this)}

2. 对于1,要让 this 指向 input,可以将 this 作为参数传递。

3. 使用addEventListener等标准DOM方法注册事件时,this指向绑定的DOM元素。


document.getElementById("test").addEventListener("click",test);

JavaScript中this的复杂场景解析

在经典面向对象语言(如C++、C#、Java)中,this关键字含义明确,始终指向当前实例。JavaScript虽也提供this关键字,但其行为比传统OO语言更为复杂多变。

接下来将深入探讨JavaScript中this在不同场景下的具体表现及其复杂性。

1. 在HTML元素事件属性中以inline方式使用this关键字:


// 可以在里面使用this  
">division element 
 // 可以在里面使用this
 ">division element 

常见做法是调用如javascript: EventHandler(this)的形式。此处可写入任何合法JavaScript语句,甚至定义内部类。其原理是脚本引擎为div实例生成一个匿名成员方法,并将onclick指向该方法。

2. 通过DOM方式在事件处理函数中使用this关键字:


division element

 var div = document.getElementById('elmtDiv');  
 div.attachEvent('onclick', EventHandler);  
 
 function EventHandler()  
 {  
 // 在此使用this  
 }  
  
// --> 
 
division element

 var div = document.getElementById('elmtDiv');
 div.attachEvent('onclick', EventHandler);

 function EventHandler()
 {
 // 在此使用this
 }
 
// --> 

此时EventHandler函数内的this指向IE浏览器的window对象。因为attachEvent调用后,脚本引擎对该函数的调用与div实例本身无直接关联。可通过this.event.srcElement获取div对象引用,且EventHandler的caller属性为null。

3. 通过DHTML方式在事件处理函数中使用this关键字:


division element
  
lt;mce:script language="javascript">
var div = document.getElementById('elmtDiv');  
div.onclick = function()  
{  
 // 在此使用this  
};  
 
/ --> 
 
division element

 var div = document.getElementById('elmtDiv');
 div.onclick = function()
 {
 // 在此使用this
 };
 
// --> 

此时this指向div元素实例。通过直接赋值div.onclick = EventHandler,相当于为div实例添加成员方法。与HTML inline方式不同,DHTML方式下脚本引擎不会再生成匿名方法。

4. 在类定义中使用this关键字:


function JSClass()  
{  
var myName = 'jsclass';  
this.m_Name = 'JSClass';  
}  
 
JSClass.prototype.ToString = function()  
{  
alert(myName + ', ' + this.m_Name);  
};  
 
var jc = new JSClass();  
jc.ToString(); 
 function JSClass()
 {
 var myName = 'jsclass';
 this.m_Name = 'JSClass';
 }

 JSClass.prototype.ToString = function()
 {
 alert(myName + ', ' + this.m_Name);
 };

 var jc = new JSClass();
 jc.ToString(); 

这是JavaScript模拟类定义中this的典型用法,与传统OO语言相似。但需注意,成员属性和方法必须使用this引用,否则如直接访问myName会提示未定义。

5. 为脚本引擎内部对象添加原型方法时的this关键字:


function.prototype.GetName = function()  
{  
var fnName = this.toString();  
fnName = fnName.substr(0, fnName.indexOf('('));  
fnName = fnName.replace(/^function/, '');  
return fnName.replace(/(^\s+)|(\s+$)/g, '');  
}  
function foo(){}  
alert(foo.GetName());  
 function.prototype.GetName = function()
 {
 var fnName = this.toString(); 
 fnName = fnName.substr(0, fnName.indexOf('(')); 
 fnName = fnName.replace(/^function/, ''); 
 return fnName.replace(/(^\s+)|(\s+$)/g, '');
 }
 function foo(){}
 alert(foo.GetName()); 

此时this指向被添加原型的类的实例,与第4点类似,无特殊之处。

6. 结合第2点与第4点,讨论一个易混淆的this使用场景:


view plaincopy to clipboardprint?
function JSClass()  
{  
this.m_Text = 'division element';  
this.m_Element = document.createElement('DIV');  
this.m_Element.innerHTML = this.m_Text;  
  
this.m_Element.attachEvent('onclick', this.ToString);  
}  
  
JSClass.prototype.Render = function()  
{  
document.body.appendChild(this.m_Element);  
}   
 
JSClass.prototype.ToString = function()  
{  
alert(this.m_Text);  
};  
 
var jc = new JSClass();  
jc.Render();  
jc.ToString(); 
 function JSClass()
 {
 this.m_Text = 'division element';
 this.m_Element = document.createElement('DIV');
 this.m_Element.innerHTML = this.m_Text;
  
 this.m_Element.attachEvent('onclick', this.ToString);
 }
  
 JSClass.prototype.Render = function()
 {
 document.body.appendChild(this.m_Element);
 } 

 JSClass.prototype.ToString = function()
 {
 alert(this.m_Text);
 };

 var jc = new JSClass();
 jc.Render(); 
 jc.ToString(); 

页面运行后首先显示“division element”,点击该文字后弹出“undefined”。

7. 在CSS expression表达式中使用this关键字:


height: expression(this.parentElement.height);">  
 division element  
  
 height: expression(this.parentElement.height);">
 division element

此处的this行为与第1点相同,均指向div元素实例本身。

8. 在函数内部嵌套函数中使用this关键字:


view plaincopy to clipboardprint?
function OuterFoo()  
{  
this.Name = 'Outer Name';  
 
function InnerFoo()  
{  
var Name = 'Inner Name';  
alert(Name + ', ' + this.Name);  
}  
return InnerFoo;  
}  
OuterFoo()(); 
 function OuterFoo()
 {
 this.Name = 'Outer Name';
 
 function InnerFoo()
 {
 var Name = 'Inner Name'; 
 alert(Name + ', ' + this.Name);
 }
 return InnerFoo;
 }
 OuterFoo()(); 

运行结果显示为“Inner Name, Outer Name”。按第2点逻辑,结果似应为“Inner Name, undefined”,但实际结果受JavaScript变量作用域影响。详细机制可参考“原来JScript中的关键字'var'还是有文章的”一文及其回复。

this用法的三类归纳与总结

综上所述,JavaScript中this的用法可归纳为以下三类(详细用法参原文):

1. 在HTML元素事件属性或CSS expression表达式中,以inline方式使用this关键字——对应原文第1、7点。

2. 在事件处理函数中使用this关键字——对应原文第2、3点。

其中又可分为两种具体方式:

(1) DOM方式:此时this指向窗口(window)对象。

(2) DHTML方式:此时this指向div元素实例。

3. 在类定义中使用this,并在其内部函数或成员函数(主要指prototype定义的方法)中使用——对应原文第4、5、8点。

需注意的是,函数本身也是对象,因此需区分变量定义与成员变量定义,具体见下方代码示例。


view plaincopy to clipboardprint?

var variableName;    //变量定义  
//作用域:函数定义范围内  
//使用方法:直接使用variableName  
this.varName;      //成员变量定义  
//作用域:函数对象定义范围内及其成员函数中  
//使用方法:this.varName 
var variableName;    //变量定义
//作用域:函数定义范围内
//使用方法:直接使用variableName
this.varName;      //成员变量定义
//作用域:函数对象定义范围内及其成员函数中
//使用方法:this.varName

基于上述三类this使用方法的归纳,对原文第6点提到的程序进行测试与改进,以验证后两种使用场景。


view plaincopy to clipboardprint?

    function JSClass()  
    {  
      var varText = "func variable!";                 //函数中的普通变量  
      this.m_Text = 'func member!';                    //函数类的成员变量  
      this.m_Element = document.createElement('DIV');   //成员变量,创建一个div对象  
      this.m_Element.innerHTML = varText;             //使用函数的普通变量  
      this.m_Element.attachEvent('onclick', this.ToString);  //给这个对象的事件连上处理函数  
      this.newElement = document.createElement('DIV');  
      this.newElement.innerHTML = "new element";   
      this.newElement.m_Text = "new element text!";      //给创建的对象建个成员  
      this.newElement.onclick = function()  
      {  
        alert(this.m_Text);                       //指向div对象的成员  
      };  
    }  
   
    JSClass.prototype.Render = function()  
    {  
      document.body.appendChild(this.m_Element);       //把div对象挂在窗口上  
      document.body.appendChild(this.newElement);  
    }    
 
    JSClass.prototype.ToString = function()  
    {  
      alert(this.m_Text);                         //指向窗口(window)对象  
    };  
 
    function initialize(){  
      var jc = new JSClass();  
      jc.Render();  
      jc.ToString();                             //里面的this指向JSClass类的实例,里面有m_Text成员  
    }  
    
// -->  

    initialize();  
    
// -->  
  
 function JSClass()
  {
   var varText = "func variable!";     //函数中的普通变量
    this.m_Text = 'func member!';     //函数类的成员变量
    this.m_Element = document.createElement('DIV'); //成员变量,创建一个div对象
    this.m_Element.innerHTML = varText;    //使用函数的普通变量
    this.m_Element.attachEvent('onclick', this.ToString); //给这个对象的事件连上处理函数
    this.newElement = document.createElement('DIV');
    this.newElement.innerHTML = "new element"; 
    this.newElement.m_Text = "new element text!";  //给创建的对象建个成员
    this.newElement.onclick = function()
   {
     alert(this.m_Text);      //指向div对象的成员
   };
  }
  
  JSClass.prototype.Render = function()
  {
    document.body.appendChild(this.m_Element);  //把div对象挂在窗口上
    document.body.appendChild(this.newElement);
  }   

  JSClass.prototype.ToString = function()
  {
    alert(this.m_Text);       //指向窗口(window)对象
  };

 function initialize(){
   var jc = new JSClass();
   jc.Render(); 
   jc.ToString();        //里面的this指向JSClass类的实例,里面有m_Text成员
  }
  
// -->

   initialize();
  
// -->

上述代码的执行结果如下:

页面加载时,弹出对话框输出“func member!”。

页面上显示文本内容。


 func variable!
 new element

单击“func variable”时,弹出对话框显示undefined。

原因是此时toString函数内的this指针指向window对象。

单击“new element”时,弹出对话框显示“new element text!”。

原因是此时toString函数内的this指针指向div元素,且该元素已定义m_Text成员(this.newElement.m_Text = "new element text!")。

实用工具推荐

电子包浆

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

使用 ›
图片压缩

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

使用 ›
相关文章