Javascript 作用域链 活动对象 执行环境 与 this 的纠结 总结~
来源:互联网 发布:苏州2.5产业园网络招聘 编辑:程序博客网 时间:2024/06/06 19:13
一篇很不错的关于Javascript中 让人蛋疼的 this问题的分析文章。因为我引的地方也不是原创,原创现在已石沉大海,所以就不引来源了。
在开始引用他的之前,我先小总结一句我感触最深的:
- 想知道this到底是谁,要看被包含函数的调用者。
- 如果是个对象,那就是该对象。
- 如果外层函数仅是作为其他函数的活动对象,被调用,那仍然是Global。也就是说,函数被直接调用执行时,不管是在哪里,其内部的this都是window~
几篇龙文,绝对的龙文:
- Javascript执行环境详解 博文
- Javascript对象模型深度剖析 博文
开始之前可以先回顾下作用域链基础也是不错的,这篇介绍的也足够详细易懂。
好,下面开始某篇较浅点的博文!开始引用::
下图是在ASP.NET中为button挂上客户端onclick事件的两种办法:图中的2和3/1。 结果发现两种方式调用同样一个函数clickMe,this却不一样。
如果采用3或1的做法,那么点击button1后将alert出[object DOMWindow];而采用2的做法,将alert出 [object HTMLInputElement](在chrome下测试。)
显然,在1的做法中,this指向DOMWindow,也就是全局的object——global;而2的做法中,this指向Button1这个元素。
为什么呢?
(注:对于3这种通过Attribute来声明处理程序的方式,button的onclick不是指向clickMe函数,而是指向一个被自动创建的匿名函数,该匿名函数以Attribute的值(也就是"clickMe();")作为函数体——也就是等价于1。)
简单的答案
基于类的OO语言(比如C#)中,函数总是声明在一个类中,函数内部的this指向该类的当前实例。而JS中,函数是第一等公民(它不会声明为别的东西的一部分),所以this跟函数是如何声明的无关,跟函数是怎么被调用的有关。
方式1和2对clickMe的调用,不同之处在于:1中clickMe是被button1.onclick所指向的函数调用的,2中clickMe是作为button1.onclick属性直接调用的。因此对于clickMe函数,1中的this指向“拥有”clickMe函数的对象——global(DOMWindow),而2中的this指向“拥有”onclick属性的对象——button1。
可惜对像我这种写JS写的不多的人来说,总是记不住这个简单答案,因为它只告诉我what,没有告诉我why。本文试图从ECMAScript官方文档出发,从原理上说明:在不同的场合中,函数的this到底是什么?
call和apply
首先明确一点,在最正常的情况下,我们这样调用:Func(),这时this是由JavaScript来确定的,这也是本篇要研究的主题。而如果用Func.call(thisArg, arg1, arg2, ...)或者Func.apply(thisArg, [arg1, arg2, ...])来调用时,this是我们自己传进去的(作为call或apply的第一个参数)。如果我们不传this进去,或者传null进去,会怎样?这时this将会是global object。
函数作为构造器时的this
先从简单的说起。所谓构造器,就是用new关键字来调用函数,在这篇关于new关键字的玄机的文章中有说到。看下面的注释,可以知道,A函数里的this这时就是a。
普通调用时的this
看下图,对于在全局定义的函数A,this就是global。
这个好理解,A作为顶级函数,是顶级对象global(也就是window)的属性,所以this === global。但看下图:
Outer()返回inner函数,Outer()()就是是对inner的调用,在inner内部,this同样等于global。可是inner函数是执行Outer时,在Outer的context下创建起来的,inner并不是global的属性(见下图)。为什么inner里的this仍旧是global?一个函数被调用时,this究竟是怎么确定的?
官方文档对this的解释
上面第1个例子,当执行A()时,这里说明了函数被调用时发生的事情,简述如下:
1, 得到A这个引用。
2, 求A的值,就是A这个函数对象。
3, A的值必须是object,并且必须有[[Call]]方法。(当然,它是函数型object,函数被创建时都有[[Call]]方法。)
4, 如果A是个引用(是),那么通过GetBase()得到A所在的对象,就是包含有A属性的对象(在我们例子中就是global)
5, 检查上面步骤中A所在的对象。如果是个activation object(就是variable object,scope chain里的对象),则把null作为thisArg传给A.call();否则就把该对象作为thisArg。
重点就是这第5步(需要对variable object熟悉,否则请先阅读这篇文章),执行A()时,global里有A属性,而global是global context下的activation object,所以将会把null作为thisArg参数传给A.call()。而上文说了,如果传的thisArg参数为null,则会把global作为this。所以gloabl === this还不是天生就这样,还转了下弯。
对于第2个例子,Outer()(),也就好解释了:因为Outer()返回的inner函数是在OuterContext中创建为OuterVariableObject的属性,所以第4步得到的对象是OuterVariableObject;那么根据第5步逻辑,仍旧把null作为thisArg传给inner.call()。因此inner里的this也同样是global。
把函数作为对象的属性值
所以,如果直接调用函数,无论这个函数是在哪个执行上下文被创建,this都指向global。除非“拥有”这个函数的对象不是一个variable object,它才会被当做this。那么很简单,我们把函数作为我们自己创建的对象(非variable object)的属性值,然后再调用,那么this就应该是这个我们创建的对象了。验证如下:
回过头看文章开头的问题:
方式1是这样调用的:Button1.onclick(clickMe();),clickMe是global的属性,因此clickMe里的this指向global。
方式2是这样调用的:Button1.onclick(),onclick是Button1的属性,因此里面的this指向Button1。
简言之,onclick和clickMe都是对真正的函数object的引用,只不过通过GetBase()得到的“所属对象”不同,this自然也就不同了。
- Javascript 作用域链 活动对象 执行环境 与 this 的纠结 总结~
- Javascript 作用域链 活动对象 执行环境 与 this 的纠结 总结~
- Javascript 作用域链 活动对象 执行环境 与 this 的纠结 总结~
- JavaScript核心:对象 原型链 构造函数 执行上下文栈 执行上下文 变量对象 活动对象 作用域链 闭包 This 总结
- 执行环境,作用域链、活动对象的关系
- JavaScript执行环境与作用域链
- JavaScript执行环境与作用域【链】
- 深入Javascript函数与闭包(执行环境、变量对象与作用域链)详解
- 深入Javascript函数与闭包(执行环境、变量对象与作用域链)使用详解
- javascript:执行环境与作用域链以及函数执行
- JavaScript作用域链、活动对象
- javascript的执行环境及作用域概念总结与理解
- JavaScript中的执行环境、变量对象和作用域链
- JavaScript变量对象、执行环境和作用域链
- 理解执行环境、作用域链和活动对象 (转载)
- JavaScript执行环境、作用域及this值
- 关于JavaScript的执行环境与作用域的解读
- JavaScript之执行环境与作用域
- 常用连接
- XP中的VMware如何与LINUX共享
- WSAStartup函数,WSACleanup,Socket接口的检索有关域名、通信服务和协议等Internet信息的数据库函数
- C#开源资源大汇总
- 各种流媒体格式说明
- Javascript 作用域链 活动对象 执行环境 与 this 的纠结 总结~
- Merge Sort的实现
- 现在用rexsee开发手机客户端越来越多了
- 关于IIS6.0中应用程序池圆设置的问题
- 基于Chrome开源提取的界面开发框架 三
- .NET实现之(简易ORM)
- distinct
- 驱动编程准备 配置内核树
- ExtJS——Grid实例