`

js Run Mechanism

    博客分类:
  • ajax
阅读更多

copy from :  
http://www.blogjava.net/JAVA-HE/archive/2010/10/09/334021.html


这个话题看似简单,其实笔者是几次三番的下笔,又几次三番的放弃。因为这个内容,对于很多JavaScript的开发人员来讲都是一知半解的,当然笔者也在其中,今天之所以出来献丑了,首先是有了更深的认识,其次微博上有人说献丑是进步,如果献丑那必定是有同道之人能指出纰漏,那对于笔者本人来讲何尝不是进步呢?深表赞同!

今天会以几个小小的实例来解读这个课题。希望能与大家共勉。

首先得先了解JavaScript执行起来的流程,笔者先简单画了一个javascript的执行流程图:



重点解释的有三步:词法分析、预解析、执行。

script代码段:用script标签分隔的js代码或引入的js文件。

(1). 预解析

我们先从几个常见的javascript 小题目入手,请大家先看看下面的范例输出什么?

<script type="text/javascript">

    alert(i); // ?

    var i = 1;

</script>

对于javascript的从业者可以试着运行下。看看你的答案和实际输出一致吗?别小看这样两行脚本,这样的题目被当作JavaScript的笔试或者面试题目是常有的事情。

实际输出结果为:“undefined”,

这种现象被称成预解析JavaScript脚本引擎优先解析var变量和function定义。在预解析完成后,才会执行代码。

由于变量是被 var声明的,而被优先解析。所以可以理解为在 alert(i) 执行时候,程序前面已经有 var i;

所以上面代码等效解释为:

<script type="text/javascript">

    var i;

alert(i); // 对于被声明,但未赋值过的i,输出‘undefined’的结果,是不应该有任何歧义了吧。

    i = 1;

</script>

注意:预解析不会报错,因为他只解析正确的声明。

(2). 解释(主要指词法分析,生成语法树的过程)

请注意,这里‘解释’的定义是笔者自己方便理解自己定义的,而这个‘解释’并不在预解析之后。

我们知道JavaScript是脚本语言,脚本语言是相对于高级编译型语言而言他是解释性的。解释性语言没有编译成二进制代码,但是要进入到运行阶段,都应该是会经过词法分析、语法分析生成语法树、语义检查过程,笔者把这个环节叫做解释,如果读者有更科学的名字记得告诉我。

解释性语言在生成语法树后,就可以执行了。(这个跟脚本引擎编译器有关)

在这个过程中,有语法检查(比如括号是否匹配),发现无法生成语法树,则报错,结束整个代码块的解析。

(3) 执行 与 作用域

引入我们的第二个示例代码:

<script type="text/javascript">

    alert(i); // error: i is not defined.

    i = 1;

</script>

听说JavaScript 变量可以直接用,那为什么还报运行时脚本错误?—— i 未定义.

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

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

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

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

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

小结

预解析,其实是在的‘解释’阶段完成,并存储在语法树中。当执行到函数实例时,会将varDelcsfuncDecls从语法树中复制到执行环境的scriptObject上。

对于示例解析:

未定义变量意味着在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的输出是什么?

  • 大小: 16.3 KB
分享到:
评论

相关推荐

    extension-mechanism-demo

    扩展机制演示这是使用延迟加载模块的Angular扩展机制的示例。构建扩展cd extensionnpm inpm run build运行应用程序cd platformnpm inpm start打开浏览器并导航到

    jdk1.8.docx

    It provides a powerful mechanism for processing collections, allowing for parallel operations, filtering, mapping, and reducing data in a functional and declarative manner. This has greatly improved ...

    jdk1.8_win64.rar

    5. **Nashorn JavaScript Engine**: JDK 1.8 includes the Nashorn JavaScript engine, allowing developers to run JavaScript code directly within the JVM. This integration facilitates interoperability ...

    java7帮助文档

    Networking Override Mechanism Security Serialization Extension Mechanism XML JAXP lang and util Base Libraries lang and util Collections Concurrency Utilities JAR Logging Management Preferences ...

    jdk-9.0.1_doc-all 最新版

    Defines the jlink tool for creating run-time images, the jmod tool for creating and manipulating JMOD files, and the jimage tool for inspecting the JDK implementation-specific container file for ...

    Microsoft SharePoint 2010 Developer Reference

    - **Feature Receivers:** Event handlers that run when features are activated or deactivated. - **Handling FeatureUpgrading Events:** Techniques for managing feature upgrades during SharePoint upgrades...

    外文翻译 stus MVC

    If you where slick, you would place the configuration information into a file and load the file at run time. Function pointer arrays were the good old days of structured programming in C. Life is ...

    php.ini-development

    Output buffering is a mechanism for controlling how much output data ; (excluding headers and cookies) PHP should keep internally before pushing that ; data to the client. If your application's ...

Global site tag (gtag.js) - Google Analytics