主页 > 前端 > javascript >
来源:自学PHP网 时间:2014-09-19 14:47 作者: 阅读:次
[导读] 在javascript作用域中,有全局作用域(Window)和函数内的作用域。...
以下变量具有全局作用域: 1.所有定义在最外层的变量(非函数体内部)具有全局作用域。 2.未定义直接赋值的变量,系统会把它声明为全局作用域。 3.所有window对象的属性具有全局作用域。
后一句定义,该变量也拥有整个函数的作用域。但是它的赋值是等到运行到那一句代码以 后才赋值的!!!
问题的提出 首先看一个例子: var name = 'laruence'; function echo() { alert(name); var name = 'eve'; alert(name); alert(age); }
echo();< li> 运行结果是什么呢? 上面的问题, 我相信会有很多人会认为是: laruence eve [脚本出错
但其实, 运行结果应该是: undefined eve [脚本出错
首先让让我们来看看Javasript(简称JS, 不完全代表JScript)的作用域的原理: JS权威指南中有一句很精辟的描述: ”JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里.”
view plaincopy to clipboardprint? 作用域链:JavaScript需要查询一个变量x时,首先会查找作用域链的第一个对象,如果以第一个对象没有定义x变量,JavaScript会继续查找有没有定义x变量,如果第二个对象没有定义则会继续查找,以此类推。 上面的代码涉及到了三个作用域链对象,依次是:inner、rainman、window。
为了接下来的知识, 你能顺利理解, 我再提醒一下, 在JS中:”一切皆是对象, 函数也是”. 在JS中,作用域的概念和其他语言差不多, 在每次调用一个函数的时候 ,就会进入一个函数内的作用域,当从函数返回以后,就返回调用前的作用域. JS的语法风格和C/C++类似, 但作用域的实现却和C/C++不同,并非用“堆栈”方式,而是使用列表,具体过程如下(ECMA262中所述): 看个例子: var func = function(lps, rps){ var name = 'laruence'; ........ } func();< li> 在执行func的定义语句的时候, 会创建一个这个函数对象的[[scope]]属性(内部属性,只有JS引擎可以访问, 但FireFox的几个引擎(SpiderMonkey和Rhino)提供了私有属性__parent__来访问它), 并将这个[[scope]]属性, 链接到定义它的作用域链上(后面会详细介绍), 此时因为func定义在全局环境, 所以此时的[[scope]]只是指向全局活动对象window active object. 在调用func的时候, 会创建一个活动对象(假设为aObj, 由JS引擎预编译时刻创建, 后面会介绍),并创建arguments属性, 然后会给这个对象添加俩个命名属性aObj.lps, aObj.rps; 对于每一个在这个函数中申明的局部变量和函数定义, 都作为该活动对象的同名命名属性. 然后将调用参数赋值给形参数,对于缺少的调用参数,赋值为undefined。 然后将这个活动对象做为scope chain的最前端, 并将func的[[scope]]属性所指向的,定义func时候的顶级活动对象, 加入到scope chain. 有了上面的作用域链, 在发生标识符解析的时候, 就会逆向查询当前scope chain列表的每一个活动对象的属性,如果找到同名的就返回。找不到,那就是这个标识符没有被定义。 注意到, 因为函数对象的[[scope]]属性是在定义一个函数的时候决定的, 而非调用的时候, 所以如下面的例子: var name = 'laruence'; function echo() { alert(name); }
function env() { var name = 'eve'; echo(); }
env();< li>
laruenc
function factory() { var name = 'laruence'; var intro = function(){ alert('I am ' + name); } return intro; }
function app(para){ var name = para; var func = factory(); func(); }
app('eve');< li>
在刚进入app函数体时, app的活动对象有一个arguments属性, 俩个值为undefined的属性: name和func. 和一个值为’eve’的属性para; 此时的scope chain如下: [[scope chain]] = [ { para : 'eve', name : undefined, func : undefined, arguments : [] }, { window call object } ]< li>
[[scope chain]] = [ { name : undefined, intor : undefined }, { window call object } ]< li>
在定义intro函数的时候, intro函数的[[scope]]为: [[scope chain]] = [ { name : 'laruence', intor : undefined }, { window call object } ]< li>
[[scope chain]] = [ { intro call object }, { name : 'laruence', intor : undefined }, { window call object } ]< li>
所以运行结果是: I am laruenc
现在, 大家对”JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里.”这句话, 应该有了个全面的认识了吧?
Javascript的预编译 我们都知道,JS是一种脚本语言, JS的执行过程, 是一种翻译执行的过程. 首先, 我们来看一个例子: <script> alert(typeof eve); //function function eve() { alert('I am Laruence'); }; </script>< li>
诶? 在alert的时候, eve不是应该还是未定义的么? 怎么eve的类型还是function呢? 恩, 对, 在JS中, 是有预编译的过程的, JS在执行每一段JS代码之前, 都会首先处理var关键字和function定义式(函数定义式和函数表达式). 而对于函数的定义,是一个要注意的地方: <script> alert(typeof eve); //结果:function alert(typeof walle); //结果:undefined function eve() { //函数定义式 alert('I am Laruence'); }; var walle = function() { //函数表达式 } alert(typeof walle); //结果:function </script>< li>
说到这里, 顺便说一个问题 : var name = 'laruence'; age = 26;< li> 我们都知道不使用var关键字定义的变量, 相当于是全局变量, 联系到我们刚才的知识: 在对age做标识符解析的时候, 因为是写操作, 所以当找到到全局的window活动对象的时候都没有找到这个标识符的时候, 会在window活动对象的基础上, 返回一个值为undefined的age属性. 也就是说, age会被定义在顶级作用域中. 现在, 也许你注意到了我刚才说的: JS在执行每一段JS代码.. <script> alert(typeof eve); //结果:undefined </script> <script> function eve() { alert('I am Laruence'); } </script>< li> 明白了么? 也就是JS的预编译是以段为处理单元的…
揭开谜底 现在让我们回到我们的第一个问题: 当echo函数被调用的时候, echo的活动对象已经被预编译过程创建, 此时echo的活动对象为: [callObj] = { name : undefined }< li>
|
自学PHP网专注网站建设学习,PHP程序学习,平面设计学习,以及操作系统学习
京ICP备14009008号-1@版权所有www.zixuephp.com
网站声明:本站所有视频,教程都由网友上传,站长收集和分享给大家学习使用,如由牵扯版权问题请联系站长邮箱904561283@qq.com