js的执行过程

作者: 叫我徐小星 | 来源:发表于2016-03-29 13:54 被阅读962次

    在网络的直言片语中禹禹前行,在书本的记录中逐步加深,然而只有在实践中才能提出疑问,才能映像深刻。


    我所理解的js执行过程
    1.按html文档流的顺序执行js代码

    HTML文档在浏览器中的解析过程是这样的:浏览器是按着文档流从上到下逐步解析页面结构和信息的。JavaScript代码作为嵌入的脚本应该也算做HTML文档的组成部分,所以JavaScript代码在装载时的执行顺序也是根据脚本标签<script>的出现顺序来确定的;

    2.预编译&执行

    当JavaScript引擎解析脚本时,它会在预编译期对所有声明的变量和函数进行处理。

    1. 在执行前会进行类似“预编译”的操作:首先会创建一个当前执行环境下的活动对象,并将那些用var申明的变量设置为活动对象的属性,但是此时这些变量的赋值都是undefined,并将那些以function定义的函数也添加为活动对象的属性,而且它们的值正是函数的定义。
    2. 在解释执行阶段,遇到变量需要解析时,会首先从当前执行环境的活动对象中查找,如果没有找到而且该执行环境的拥有者有prototype属性时则会从prototype链中查找,否则将会按照作用域链查找。遇到var a = ...这样的语句时会给相应的变量进行赋值(注意:变量的赋值是在解释执行阶段完成的,如果在这之前使用变量,它的值会是undefined)
    function Hello(){
      alert("Hello");
    }
    Hello();//Hello World
    
    function Hello() {
      alert("Hello World");
    }
    Hello();//Hello World
    

    会看到输出两次:Hello World
    其实上面的一段代码其实被JS引擎预编译为这样的形式:

    var Hello = function() {
      alert("Hello");
    }
    Hello = function() {
      alert("Hello World");
    }
    Hello();
    Hello();
    

    我们可以看到,其实函数也是数据,也是变量,我们也可以对“函数“进行赋值(重赋值)。

    alert(a);//undefined    
    var a=1;    
    alert(a);   //1
    

    :变量初始化过程发生在执行期,而不是预编译期。在执行期,JavaScript解释器是按着代码先后顺序进行解析的,如果在前面代码行中没有为变量赋值,则JavaScript解释器会使用默认值undefined。由于在第二行中为变量a赋值了,所以在第三行代码中会提示变量a的值为1,而不是undefined。

    比较以下两段代码

    f();  // 调用函数,返回值1    
    function f(){    
        alert(1);    
    } 
    
    f(); // 调用函数,返回语法错误(缺少对象)    
    var f= function(){    
        alert(1);    
    } 
    

    之所以结果不同是因为:在下面一段中,定义的函数仅作为值赋值给变量f,所以在预编译期,JavaScript解释器只能够为声明变量f进行处理,而对于变量f的值,只能等到执行期时按顺序进行赋值,自然就会出现语法错误,提示找不到对象f。

    虽然变量和函数声明可以在文档任意位置,但是良好的习惯应该是在所有JavaScript代码之前声明全局变量和函数,并对变量进行初始化赋值。在函数内部也是先声明变量,然后再引用。

    3. 借助事件机制改变JavaScript执行顺序

    就是浏览器在解析HTML文档流时,如果遇到一个<script>标签,则JavaScript解释器会等到这个代码块都加载完后,先对代码块进行预编译,然后再执行。执行完毕后,浏览器会继续解析下面的HTML文档流,同时JavaScript解释器也准备好处理下一个代码块。

    由于JavaScript是按块处理代码,同时又遵循HTML文档流的解析顺序,所以有时第一次执行代码会出现错误。但是当文档流加载完毕,如果再次访问就不会出现这样的错误。

    <script>    
    //JavaScript代码块1    
    window.onload= function(){        // 页面初始化事件处理函数    
        alert(a);    
        f();    
    }    
    </script>    
    <script>    
    //JavaScript代码块2    
    var a =1;    
    functionf(){    
        alert(1);    
    }    
    </script>  
    

    此时把访问第2块代码中的变量和函数的代码放在页面初始化事件函数中,就不会出现语法错误了。

    为了安全起见,我们一般在页面初始化完毕之后才允许JavaScript代码执行,这样可以避免网速对JavaScript执行的影响,同时也避开了HTML文档流对于JavaScript执行的限制。

    以上包含了函数的声明前置,编译与预编译,我们知道正常的一段js代码都是先进性预编译,然后从上到下顺序执行。
    后来偶然一次发现:

    function A(){
    ...
    }
    function B(){
    ...
    }
    A();
    B();
    

    类似以上这样一段,通过断点发现有时候函数A没有执行完就开始执行函数B;当时百思不得其解,首先确认的是:js是不是顺序执行??得到的结果是。既然是,为什么会有时先执行B?后来旁边的实习生随意说了句:ajax的影响吧!顿时恍然大悟,我竟然忘了我的函数里面都用了ajax向后台异步请求数据,真是囧!

    jq的ajax有两种方式:
    同步:当JS代码加载到当前AJAX的时候会把页面里所有的代码停止加载,页面出先假死状态,当这个AJAX执行完毕后才会继续运行其他代码页面假死状态解除。
    异步:AJAX代码运行的时候其他代码一样可以运行。
    async:false//同步
    async:true//异步(默认)

    相关文章

      网友评论

      • delphin:好文章,变量和函数提升的差别:变量先在预定义阶段设为undefined,执行阶段再赋值;函数则是在预定义阶段直接定义和赋值。非常好,受教了!
      • 一俢:喜欢
      • 我在睡觉:函数a没有执行完就执行函数b的说法不是很准确,函数a执行完了哦。异步的ajax请求立即返回了,ajax之下的函数语句也得到执行了,才执行b函数的。只是请求的响应还没有回来而已。👽
        叫我徐小星:@我在睡觉 谢谢你的提醒与热心。我会在这样的声音里越行越远 :blush:
        我在睡觉:@叫我徐小星 异步请求的话下面的代码是不应该访问返回数据的,访问数据的代码写在回调函数里。同步请求的话可以。你写代码实测一下吧,会加深理解。把浏览器调成龟速模式比较容易看出异步的效果
        叫我徐小星:@我在睡觉 也就是说ajax发送请求等待接收数据的时候会继续往下执行,所以请求发送成功后要调用的函数就不会立即执行,在接收数据的同时,代码也在继续往下,所以此期间如果用用到返回的数据的,都会是undefined吗?

      本文标题:js的执行过程

      本文链接:https://www.haomeiwen.com/subject/ejhslttx.html