专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »Javascript教程 » JavaScript教程:浅析JS运行机制 »正文

JavaScript教程:浅析JS运行机制

来源: 发布时间:星期六, 2009年2月14日 浏览:0次 评论:0
="t18">从个简单问题谈起:

<script type="text/javascript">
alert(i); // ?
var i = 1;
</script>
输出结果是und, 这种现象被称成“预解析”:JavaScript引擎会优先解析var变量和function定义在预解析完成后才会执行代码如果个文档流中包含多个script代码段(用script标签分隔js代码或引入js文件)运行顺序是:

step1. 读入第个代码段
step2. 做语法分析有错则报语法(比如括号不匹配等)并跳转到step5
step3. 对var变量和function定义做“预解析”(永远不会报错只解析正确声明)
step4. 执行代码段有错则报错(比如变量未定义)
step5. 如果还有下个代码段则读入下个代码段重复step2
step6. 结束
上面分析已经能解释很多问题了但老觉得欠缺点什么比如step3里“预解析”究竟是如何回事?还有step4里看下面例子:

<script type="text/javascript">
alert(i); // error: i is not d.
i = 1;
</script>
为什么第句会导致?JavaScript中变量不是可以不定义吗?

编译过程
时间如白马过隙书柜旁翻开恍如隔世般编译原理熟悉而又陌生空白处有着这样笔记:

对于传统编译型语言来说编译步骤分为:词法分析、语法分析、语义检查、代码优化和字节生成
但对于解释型语言来说通过词法分析和语法分析得到语法树后就可以开始解释执行了
简单地说词法分析是将流(char stream)转换为记号流(token stream), 比如将c = a - b;转换为:

NAME "c"
EQUALS
NAME "a"
MINUS
NAME "b"
SEMICOLON
上面只是举例更进了解请查看 Lexical Analysis.

JavaScript权威指南第2章就是词法结构(Lexical Structure)ECMA-262 中也有描述词法结构是门语言基础很容易掌握至于词法分析实现那是另个研究领域在此不探究

可以拿自然语言来类比词法分析是硬性翻译比如段英文逐词翻译成中文得到堆记号流还很难理解翻译就需要语法分析了下图是个条件语句语法树:




构造语法树时候如果发现无法构造比如(a { i = 2; }, 就会报语法并结束整个代码块解析这就是本文开头部分step2.

通过语法分析构造出语法树后翻译出来句子可能还会有模糊不清地方接下来还需要进语义检查对于传统强类型语言来说语义检查主要部分是类型检查比如实参和形参类型是否匹配对于弱类型语言来说步可能没有(精力有限没时间去看JS引擎实现不敢确定JS引擎中是否有语义检查这步)

通过上面分析可以看出对于JavaScript引擎来说肯定有词法分析和语法分析的后可能还有语义检查、代码优化等步骤等这些编译步骤完成的后(任何语言都有编译过程只是解释型语言没有编译成 2进制代码)才会开始执行代码

上面编译过程还是无法更深入解释文章开头部分“预解析”我们还得仔细探究下JavaScript代码执行过程

执行过程
周爱民在JavaScript语言精髓和编程实战第 2部分对此有非常仔细分析下面是我些领悟:

通过编译JavaScript代码已经翻译成了语法树然后会立刻按照语法树执行

执行过程需要理解JavaScript作用域机制JavaScript采用是词法作用域(lexcical scope)通俗地讲就是JavaScript变量作用域是在定义时决定而不是执行时决定也就是说词法作用域取决于源码编译器通过静态分析就能确定因此词法作用域也叫做静态作用域( scope)但需要注意with和eval语义无法仅通过静态技术实现实际上只能说JS作用域机制非常接近lexical scope.

JS引擎在执行每个例子时都会创建个执行环境(execution context)execution context中包含对象(call object), 对象是个scriptObject结构用来保存内部变量表varDecls、内嵌表funDecls、父级引用列表upvalue等语法分析结构(注意:varDecls和funDecls等信息是在语法分析阶段就已经得到并保存在语法树中例子执行时会将这些信息从语法树复制到scriptObject上)scriptObject是和相关套静态系统例子生命周期保持

lexical scope是JS作用域机制还需要理解它实现思路方法这就是作用域链(scope chain)scope chain是个name lookup机制首先在当前执行环境scriptObject中寻找没找到则顺着upvalue到父级scriptObject中寻找直lookup到全局对象(global object)

例子执行时会创建或关联到个闭包(closure) scriptObject用来静态保存和相关变量表closure则在执行期动态保存这些变量表及其运行值closure生命周期有可能比例子长例子在活动引用为空后会自动销毁closure则要等要数据引用为空后由JS引擎回收(有些情况下不会自动回收就导致了内存泄漏)

别被上面堆名词吓住旦理解了执行环境、对象、闭包、词法作用域、作用域链这些概念JS语言很多现象都能迎刃而解

小结
至此对于文章开头部分疑问可以解释得很清楚了:

step3中所谓“预解析”其实是在step2语法分析阶段完成并存储在语法树中当执行到例子时会将varDelcs和funcDecls从语法树中复制到执行环境scriptObject上

step4中未定义变量意味着在scriptObject变量表中找不到JS引擎会沿着scriptObjectupvalue往上寻找如果都没找到对于写操作i = 1; 最后就会等价为 window.i = 1; 给window对象新增了个属性对于读操作如果直追溯到全局执行环境scriptObject上都找不到就会产生运行期

理解后雾散花开天空片晴朗

最后留个问题给大家:

<script type="text/javascript">
var arg = 1;
function foo(arg) {
alert(arg);
var arg = 2;
}
foo(3);
</script>
请问alert输出是什么?

标签:
0

相关文章

读者评论

发表评论

  • 昵称:
  • 内容: