- 浏览: 193598 次
- 性别:
- 来自: QD
文章分类
- 全部博客 (127)
- Struts2 (8)
- Web (27)
- 计算机基础 (2)
- 面试 (2)
- JQuery (4)
- MySQL (1)
- SQL (1)
- AJAX (3)
- Java (17)
- Javascript (36)
- 情感 (0)
- Oracle (7)
- Spring (5)
- FreeMarker (2)
- JSON (1)
- 表达式 (1)
- 线程 (4)
- WebService (10)
- MyEclipse (2)
- LDAP (1)
- Tomcat (1)
- NIO (1)
- Linux (1)
- ExtJS (4)
- Android (1)
- Dojo (2)
- Maven (9)
- Ant (7)
- 分布式 (1)
- Intellij IDEA (1)
最新评论
-
northc:
米饭军 写道如果文件已存在怎样避免应该会覆盖的
用Ant scp往远程linux传文件 -
米饭军:
如果文件已存在怎样避免
用Ant scp往远程linux传文件 -
luis025:
不支持列隐藏 硬伤
ExtJS4.0 分享Grid导出Excel插件(3.28更新支持4.1) -
rgbhje:
楼主大神,我把你的代码简单改了一小下,在4.2可以跑了
ExtJS4.0 分享Grid导出Excel插件(3.28更新支持4.1) -
rgbhje:
楼主大神,我把你的代码简单改了一小下,在4.2可以跑了
ExtJS4.0 分享Grid导出Excel插件(3.28更新支持4.1)
from:http://wenku.baidu.com/view/a4290ad9ce2f0066f5332229.html
关于js的作用域链,早有耳闻,也曾看过几篇介绍性的博文,但一直都理解的模棱两可。近日又精心翻看了一下《悟透Javascript》这本书,觉得写得太深刻,在“代码的时空”一节里有一段介绍作用域链的地方寥寥数语,回味无穷(其实还是理解的模棱两可^_^)。现在整理下自己的读书笔记,顺便借鉴网上资源,写下来。
一、从一个简单的问题说起
下面的js代码在页面中运行显示什么结果:
var arg= 1;
function fucTest(arg) {
alert(arg);
var arg= 2;
//alert(arg);
}
fucTest(10);
您的答案是什么?没错,就是弹出10。我的理解是这样的,funTest函数有一个形参arg,funTest函数传入实参10,alert方法把10弹出就是了,囧。
好,问题又来了:
var arg= 1;
function funcTest() {
alert(arg);
var arg= 2;
}
arg= 10;
funcTest();
答案是什么?如果是5年前的我,肯定不会再往下想了,还是10!这么简单的问题还用想什么呀?我的理解是这样的:funTest函数是一个无参数的函数,函数内部通过alert方法,调用外部(全局)的变量arg,在函数执行前,arg赋值为10,弹出arg值后改变arg值为2,所以弹出值为10。
真的是10吗?是还是不是?
测试的结果:弹出“undefined”,瀑布汗.
二、理解作用域链,从javascript运行机制说起
1、js的运行顺序
如果一个文档流中包含多个script代码段(用script标签分隔的js代码或引入的js文件),它们的运行顺序是:
步骤1. 读入第一个代码段(js执行引擎并非一行一行地执行程序,而是一段一段地分析执行的)
步骤2. 做语法分析,有错则报语法错误(比如括号不匹配等),并跳转到步骤5
步骤3. 对var变量和function定义做“预解析”(永远不会报错的,因为只解析正确的声明)
步骤4. 执行代码段,有错则报错(比如变量未定义)
步骤5. 如果还有下一个代码段,则读入下一个代码段,重复步骤2
步骤6. 结束
上面的分析已经足够清楚,步骤二、三和步骤四里的红色字体可能是我们新手理解上的一个盲点,尤其是步骤三的“预解析”,如果不清楚什么叫预解析,总觉得不踏实。而步骤四的“有错则报错”也是经常碰到的。举例来说:
function funcTest() {
alert(arg);
var arg= 2;
}
funcTest();
上面这段代码执行时,弹出“undefined”,也就是说arg没有定义,js的变量不是不用定义也可以吗?
2、语法分析和“预解析”
(1)、从解释型语言的编译过程说起
众所周知,javascript是解释型语言,它不同于c#和java等编译型语言。对于传统编译型语言来说,编译步骤分为:词法分析、语法分析、语义检查、代码优化和字节生成;但对于解释型语言来说,通过词法分析和语法分析得到语法树后,就可以开始解释执行了。
a、词法分析
简单地说,词法分析是将字符流(char stream)转换为记号流(token stream)。
但是这个转换过程并不是可以用一句话就可以概括的那么简单,我们可以试着用伪代码理解一段简单的程序:
代码var result=x-y;的转换大致可以表示如下:
NAME "result"
EQUALS
NAME "x"
MINUS
NAME "y"
SEMICOLON
b、语法分析
简单地说,语法分析就是为了构造合法的语法分析树,而语法分析树可以直观地表示出推导的过程。
那么什么是语法分析树?简单地说,就是程序推导过程的描述。但是到底什么是语法树,请参考专业文章,本篇略过。
c、其他
通过语法分析,构造出语法分析树后,接下来还可能需要进一步的语义检查。对于传统强类型语言来说,语义检查的主要部分是类型检查,比如函数的实参和形参类型是否匹配等等。
结论:通过上面的分析可以看出,对于javascript引擎来说,肯定有词法分析和语法分析,之后可能还有语义检查、代码优化等步骤,等这些编译步骤完成之后(任何语言都有编译过程,只是解释型语言没有编译成二进制代码),才会开始执行代码。
(2)、执行过程
a、javascript的作用域机制
通过编译,javascript代码已经翻译成了语法树,然后会立刻按照语法树执行。
进一步的执行过程,需要理解javascript的作用域机制:词法作用域(lexcical scope)。通俗地讲,就是javascript变量的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源码,编译器通过静态分析就能确定,因此词法作用域也叫做静态作用域(static scope)。但需要注意,with和eval的语义无法仅通过静态技术实现,所以只能说javascript的作用域机制非常接近词法作用域(lexical scope).
javascript引擎在执行每个函数实例时,都会创建一个执行环境(execution context)。执行环境中包含一个调用对象(call object), 调用对象是一个scriptObject结构(scriptObject是与函数相关的一套静态系统,与函数实例的生命周期保持一致),用来保存内部变量表varDecls、内嵌函数表funDecls、父级引用列表upvalue等语法分析结构(注意varDecls和funDecls等信息是在语法分析阶段就已经得到,并保存在语法树中。函数实例执行时,会将这些信息从语法树复制到scriptObject上)。
b、javascript作用域机制的实现方法
词法作用域(lexical scope)是javascript的作用域机制,还需要理解它的实现方法,就是作用域链(scope chain)。作用域链是一个name lookup机制,首先在当前执行环境的scriptObject中寻找,没找到,则顺着upvalue到父scriptObject中寻找,一直lookup到全局调用对象(global object)。
现在回过头来分析第二个问题:
var arg= 1;
function funcTest() {
alert(arg);
var arg= 2;
}
arg= 10;
funcTest();
在执行funcTest函数时,也即进入了funcTest对应的作用域,js引擎在执行时,当遇到对变量名或者函数名的使用时,会首先在当前作用域(也即funcTest对应的作用域)查找变量或者函数(显然,arg变量在funcTest对应的作用域里被定义为var arg=2 所以alert方法的参数采用的是当前作用域的arg,但是因为arg被定义在alert方法后,所以arg变量默认值为undefined)。当然,如果没有找到就到上层作用域查找,依此类推(作用域范围可以持续到javascript运行环境的根:window对象)。
最后,让你看的更清楚,上面的代码其实可以等价于:
var arg= 1;
function funcTest() {
var arg; //默认值undefined
alert(arg);
arg= 2;
}
arg= 10;
funcTest();
c、闭包(closure)
当一个函数实例执行时,会创建或关联到一个闭包。 (关于闭包,打算另写一篇学习笔记)
scriptObject用来静态保存与函数相关的变量表,闭包则在执行期动态保存这些变量表及其运行值;
闭包的生命周期有可能比函数实例长。函数实例在活动引用为空后会自动销毁;
闭包则要等要数据引用为空后,由javascript引擎回收(有些情况下不会自动回收,就导致了内存泄漏)。
ps:关于“执行过程”这一段比较拗口,名词很多,不过别被它们吓住,一旦理解了执行环境(execution context)、调用对象(call object)、词法作用域(lexical scope)、作用域链(scope chain)、闭包(closure)等这些概念,javascript的很多现象都能迎刃而解。
三、结语
通过第二段的分析,对照第一段笔者曾经做出的判断(你是不是也觉得笔者曾经的分析和结论很幼稚(哪怕有时结果碰巧也对!)?!不是一般的肤浅啊,^_^),你会发现原来javascript还有这么多“玄机”,而要真正理解精通又谈何容易?先“悟透”再说吧。
发表评论
-
Ext Toolbar换行
2012-06-21 09:02 01.var oneTbar=new Ext.Toolba ... -
禁止select控件選擇
2012-06-20 20:06 0禁止select控件選擇 大家知道, 對於HTML控件 ... -
几种压缩算法原理介绍
2012-05-25 20:04 1359先给出一个JS实现的ZIP:http://stuartk ... -
ExtJS4.0 分享Grid导出Excel插件(3.28更新支持4.1)
2012-03-12 17:00 10879需要将ExtJS Grid 导出 Excel 的同学可以试一试 ... -
Ext Js 4.x 扩展自己的XType
2012-03-03 14:26 1553如果想用自己定制的XType(比如这里想用一个Obj存 ... -
(转)Javascript中大括号“{}”的多义性
2012-02-25 11:21 1010JS中大括号有四种语义作用语义1,组织复合语句,这是最常 ... -
function sleep
2012-02-17 19:16 883//毫秒 (function sleep(t){ ... -
不用递归,循环算0-9999之和(js)
2012-02-17 19:15 892var i=0,sum=eval(new Arra ... -
Augment.js 为旧浏览器增加现代Javascript的功能支持
2012-01-18 12:06 1071index: http://olivernn. ... -
Javascript中的~和~~
2011-12-30 15:58 880<script type="text/j ... -
js中的onchange和onpropertychange
2011-12-28 11:26 1842当一个HTML元素的属 ... -
parentNode、parentElement,childNodes、children
2011-12-26 17:38 929parentNode、parentElement,childN ... -
关于JS中的constructor与prototype
2011-12-22 19:53 1011我们都知道,在JS中有一个function的东西。一般人 ... -
各浏览器对document.getElementById等方法的实现差异
2011-12-21 13:15 931本文来自:http://www.cnblogs.com/sna ... -
全世界最短的IE判定
2011-12-20 15:45 802var ie = !-[1,]; ... -
document.getElementsByClassName的理想实现
2011-12-20 14:41 1436来自: 司徒正美 blog http://www.cnblo ... -
浏览器支持的JS版本及JS对象图
2011-12-20 10:38 1081来自zh.wikipedia.org的JavaScript ... -
JS判断浏览器能力
2011-12-15 20:47 1050对象/特征检测法 该方法是一种判断浏览器能力(而非浏览器 ... -
onunload和onbeforeunload区别
2011-12-15 18:54 972Onunload,onbeforeunload都是在刷 ... -
收藏的两个多tab切换
2011-12-13 19:02 950留着可能以后有用
相关推荐
深入理解JavaScript作用域和作用域链对于编写高效、无错的代码至关重要。正确管理作用域可以避免全局变量冲突,提高代码的复用性和模块化,同时也有助于提升性能,因为局部变量的访问速度通常比全局变量快。在实际...
JavaScript中的作用域链是理解变量查找和闭包的关键概念。作用域链主要涉及到函数执行上下文和全局执行上下文中的作用域。以下是对这个主题的详细解释: 首先,每个函数在创建时,都会有一个内部属性[[scope]],它...
JavaScript中的作用域链是编程中一个至关重要的概念,它决定了变量和函数的可访问性以及在不同作用域内的查找顺序。在深入理解作用域链之前,我们首先要了解什么是执行环境和变量对象。 执行环境,简单来说,就是...
### JavaScript作用域链(Scope Chain)初探 #### 一、引言 JavaScript的作用域链是一个重要的概念,尤其是在深入理解JavaScript执行机制时不可或缺的一部分。本文将通过对几个具体例子的分析来探讨JavaScript作用域...
作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理。今天这篇文章对JavaScript作用域和作用域链作简单的介绍,希望能帮助大家更好的学习JavaScript。任何程序...
JavaScript 变量作用域是基于其特有的作用域链的。在 JavaScript 中,变量作用域是指变量可以被访问和修改的范围。 JavaScript 没有块级作用域,而是基于函数作用域和全局作用域的。 首先,看看 JavaScript 的作用...
JavaScript的作用域链是理解JavaScript变量查找和闭包的关键概念。以下是对这八个点的详细解释: 1. **定义时作用域链与运行时作用域链**: - 定义时作用域链([[Scope]])是在函数创建时确定的,它记录了函数声明...
在JavaScript编程中,作用域和作用域链是影响程序性能的关键因素之一。优化作用域链是提高JavaScript代码执行效率的重要手段。下面我们从多个方面来详细探讨作用域链的概念、作用域链的层级关系、变量访问的性能影响...
JavaScript中的闭包、匿名函数和作用域链是编程中至关重要的概念,它们是理解JavaScript运行机制的关键。在本文中,我们将深入探讨这三个概念,并通过实际示例来展示它们的运用。 首先,我们来讨论“闭包”。闭包是...
JavaScript的作用域链是理解JavaScript执行环境的关键概念,它决定了变量和函数的可访问性。在JavaScript中,每个函数都有自己的作用域,而这些作用域按照特定的顺序组织起来,形成了作用域链。这个链帮助解析器在...
作用域链是JavaScript在查找变量时遵循的一个规则集合。当尝试访问一个变量时,JavaScript引擎会首先在当前作用域中查找,如果找不到,就会沿着作用域链向上层作用域查找,直到找到变量或者到达全局作用域。如果在...
JavaScript是一种广泛用于网页和网络应用的脚本语言,它的核心特性包括原型链和作用域链。这两个概念是理解JavaScript中对象继承和变量访问的关键。 **作用域链** 1. **作用域生成**:每次JavaScript代码执行时,...
整个作用域链是由不同执行位置上的 Variable Object 按照规则所构建一个链表。作用域链的最前端,始终是当前正在执行的代码所在环境的 Variable Object。如果这个环境是函数(比如 Fn2),则将其活动对象...
JavaScript作用域是指在JavaScript代码中,变量、常量、对象和函数能够访问的范围。在编程中,变量和函数的使用都受到作用域的限制,决定了它们能够在哪些代码块中被引用。作用域有助于防止变量命名冲突,也使得程序...
总的来说,深入理解JavaScript的作用域和作用域链对于编写高效、无bug的代码至关重要。通过掌握这些基本概念,开发者可以更好地管理变量和函数的生命周期,避免全局变量的滥用,并有效地组织代码逻辑。
JavaScript 作用域链是 JavaScript 语言中一个至关重要的概念,它决定了变量和函数的访问权限。在 JavaScript 中,每个函数都有自己的作用域,也就是变量和函数的可见范围。当一个函数被创建时,它会形成一个作用域...