this 关键字相信大家都非常熟悉,js 中提供的 this 关键在要比 oo 语言中混乱,下面通过本文给大家介绍 js 里 this 关键字的理解,需要的朋友可以参考下
Javascript 是一种由 Netscape 的 LiveScript 发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如 Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
this 关键字在 c++,java 中都提供了这个关键字,在刚开始学习时觉得有难度,但是只要理解了,用起来就方便多了,下面通过本篇文章给大家详解 js 里 this 关键字的理解。
关于 this,是很多前端面试必考的题目,有时候在网上看到这些题目,自己试了一下,额,还真的错了!在实际开发中,也会遇到 this 的问题(虽然一些类库会帮我们处理),例如在使用一些框架的时候,例如: knockout,有时候不明白为什么不直接使用 this, 而要把 this 作为参数传入。
接下来你谈谈我对它的理解,也作为一个笔记,方便以后参阅。有不对的地方,欢迎指出批评。
1. 不像 C#,this 一定是指向当前对象。
js 的 this 指向是不确定的,也就是说是可以动态改变的。call/apply 就是用于改变 this 指向的函数,这样设计可以让代码更加灵活,复用性更高。
2. 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.number=3; this 指向的就是 obj 的属性 number。
同理,第二个 console.log 打印的也是属性 number。
为什么第二点说一般情况下 this 都是指向函数的拥有者,因为有特殊情况。函数自执行就是特殊情况,在函数自执行里,this 指向的是:window。所以第一个 console.log 打印的是 window 的属性 number。
所以要加一点:
3. 在函数自执行里,this 指向的是 window 对象。
扩展,关于 this,还有一个地方比较让人模糊的是在 dom 事件里,通常有如下 3 种情况:
如下:
- <input id="test" type="button" value="按钮" onClick="test()"/>
- function test(){alert(this)}
2. 对于 1,要让 this 指向 input,可以将 this 作为参数传递。
3. 使用 addEventListener 等注册。此时 this 也是指向 input。
- document.getElementById("test").addEventListener("click",test);
在面向对象编程语言中,对于 this 关键字我们是非常熟悉的。比如 C++、C# 和 Java 等都提供了这个关键字,虽然在开始学习的时候觉得比较难,但只要理解了,用起来是非常方便和意义确定的。JavaScript 也提供了这个 this 关键字,不过用起来就比经典 OO 语言中要 "混乱" 的多了。
下面就来看看,在 JavaScript 中各种 this 的使用方法有什么混乱之处?
1、在 html 元素事件属性中 inline 方式使用 this 关键字:
- // 可以在里面使用this
- ">division element
- // 可以在里面使用this
- ">division element
我们一般比较常用的方法是在此使用:javascirpt: 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 对象。这是因为 EventHandler 只是一个普通的函数,对于 attachEvent 后,脚本引擎对它的调用和 div 对象本身没有任何的关系。同时你可以再看看 EventHandler 的 caller 属性,它是等于 null 的。如果我们要在这个方法中获得 div 对象引用,应该使用:this.event.srcElement。
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 元素对象实例,在脚本中使用 DHTML 方式直接为 div.onclick 赋值一个 EventHandler 的方法,等于为 div 对象实例添加一个成员方法。这种方式和第一种方法的区别是,第一种方法是使用 HTML 方式,而这里是 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",确定后点击文字 "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'还是有文章的" 一文及回复。
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!")
来源: