zoukankan      html  css  js  c++  java
  • javascript在html中的加载顺序

    参考:[1]http://coolshell.cn/articles/9749.html(酷壳)

             [2]http://shaozhuqing.com/?p=2756

    颜色标注分别来自于链接地址内容


        通常来说,浏览器对于Javascript的运行有两大特性:1)载入后马上执行,2)执行时会阻塞页面后续的内容(包括页面的渲染、其它资源的下载)。于是,如果有多个js文件被引入,那么对于浏览器来说,这些js文件被被串行地载入,并依次执行。

      因为javascript可能会来操作HTML文档的DOM树,所以,浏览器一般都不会像并行下载css文件并行下载js文件,因为这是js文件的特殊性造成的。所以,如果你的javascript想操作后面的DOM元素,基本上来说,浏览器都会报错说对象找不到。因为Javascript执行时,后面的HTML被阻塞住了,DOM树时还没有后面的DOM结点。所以程序也就报错了。

    传统的方式

    <script type="text/javascript"        src="http://coolshell.cn/asyncjs/alert.js"></script>

      基本上来说,head里的 <script>标签会阻塞后续资源的载入以及整个页面的生成。(在酷壳的主页中有这个例子) 。所以,你知道为什么有很多网站把javascript放在网页的最后面了,要么就是动用了window.onload或是docmuemt ready之类的事件。

    这是因为 放在<head>标签中的javascript会在页面一旦打开的时候进行加载,因此,如果这里面的js对DOM树进行加载的话,很容易就会因为找不到!

    怎样实现等待DOM被完全加载后才调用JS文件

    (1) 将所有的JS文件都放在</HTML>后面后再进行调用

    (2) 将JS代码放在标签里面

    看下面的代码:

    <html>
    <head>
    <script type="text/javascript" src="js/jquery-1.9.0.min.js"></script>
    
    </head>
    <script>
    function test(){
    	$('#mybtn').click(function(){
    	alert("123")
    	});
    }
    window.onload=test();
    </script>
    <body>
    <div id="toolbar">
    	<input type='button' value="click me" id="mybtn"></input>
    </div>
    <div id="myDiv" style="height:200px;200px;background-color:#232345">
    </div>
    </body>
    </html>
    

     $('#mybtn')会因为找不到mybtn的标签而报错

    修改代码如下:

    <html>
    <head>
    <script type="text/javascript" src="js/jquery-1.9.0.min.js"></script>
    
    </head>
    
    <body>
    <div id="toolbar">
    	<input type='button' value="click me" id="mybtn"></input>
    </div>
    <div id="myDiv" style="height:200px;200px;background-color:#232345">
    </div>
    </body>
    </html>
    <script>
    function test(){
    	$('#mybtn').click(function(){
    	alert("123")
    	});
    }
    window.onload=test();
    </script>
    

     可以顺利实现弹出alert,另外,通过事件的方法也可以改变JS的调用顺序

    <html>
    <head>
    <script type="text/javascript" src="js/jquery-1.9.0.min.js"></script>
    
    </head>
    <script>
    function test(){
    	$('#mybtn').click(function(){
    	alert("123")
    	});
    }
    </script>
    <body onload="test()">
    <div id="toolbar">
    	<input type='button' value="click me" id="mybtn"></input>
    </div>
    <div id="myDiv" style="height:200px;200px;background-color:#232345">
    </div>
    </body>
    </html>
    

    因为绝大多数的Javascript代码并不需要等页面,所以,我们异步载入的功能。那么我们怎么异步载入呢?

    http://coolshell.cn/articles/9749.html(酷壳)


    预编译

    请先看下面的一段代码!

     1 <scripttype="text/javascript">
     2 
     3         functionHello() {
     4 
     5             alert("Hello");
     6 
     7         }
     8 
     9         Hello();
    10 
    11         functionHello() {
    12 
    13             alert("Hello World");
    14 
    15         }
    16 
    17         Hello();
    18 
    19     </script>

    输出结果并不是我们想象的那样,而是两次的输出结果都是hello,world!

    这是因为Javascript并非完全的按顺序解释执行,而是在解释之前会对Javascript进行一次“预编译”,在预编译的过程中,会把定义式的函数优先执行,也会把所有var变量创建,默认值为undefined,以提高程序的执行效率。也就是说上面的一段代码其实被JS引擎预编译为这样的形式:

     1  <scripttype="text/javascript">
     2 
     3         varHello = function() {
     4 
     5             alert("Hello");
     6 
     7         }
     8 
     9         Hello = function() {
    10 
    11             alert("Hello World");
    12 
    13         }
    14 
    15         Hello();
    16 
    17         Hello();
    18 
    19     </script>

    后面的hello会把前面的hello给覆盖掉,因此会出现两次执行的结果都是hello,world.

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

    做如下处理:

    1. 在执行前会进行类似“预编译”的操作:首先会创建一个当前执行环境下的活动对象,并将那些用var申明的变量设置为活动对象的属性,但是此时这些变量的赋值都是undefined,并将那些以function定义的函数也添加为活动对象的属性,而且它们的值正是函数的定义。

    2. 在解释执行阶段,遇到变量需要解析时,会首先从当前执行环境的活动对象中查找,如果没有找到而且该执行环境的拥有者有prototype属性时则会从prototype链中查找,否则将会按照作用域链查找。遇到var a = ...这样的语句时会给相应的变量进行赋值(注意:变量的赋值是在解释执行阶段完成的,如果在这之前使用变量,它的值会是undefined) 所以,就会出现当JavaScript解释器执行下面脚本时不会报错:

    alert(a);                            // 返回值undefined

    var a =1;

    alert(a);                            // 返回值1

    由于变量声明是在预编译期被处理的,所以在执行期间对于所有代码来说,都是可见的。但是,你也会看到,执行上面代码,提示的值是undefined,而不是1。这是因为,变量初始化过程发生在执行期,而不是预编译期。在执行期,JavaScript解释器是按着代码先后顺序进行解析的,如果在前面代码行中没有为变量赋值,则JavaScript解释器会使用默认值undefined。由于在第二行中为变量a赋值了,所以在第三行代码中会提示变量a的值为1,而不是undefined。

    同理,下面示例在函数声明前调用函数也是合法的,并能够被正确解析,所以返回值为1。

     1 f();                                 // 调用函数,返回值1
     2 
     3 function f(){
     4 
     5     alert(1);
     6 
     7 }
     8 
     9 但是,如果按下面方式定义函数,则JavaScript解释器会提示语法错误。
    10 
    11 f();                                 // 调用函数,返回语法错误
    12 
    13 var f = function(){
    14 
    15     alert(1);
    16 
    17 }

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

     

    总结: JS加载包含预编译和执行两个阶段。 编译阶段会对所有的var变量和function进行扫描,并将var变量初始化为undefined类型,而function则被初始化为函数值。

       到了执行阶段,JS从上面往下面依顺序执行,遇到var变量便进行赋值(因此,在赋值之前进行调用的话会出现错误).遇到函数变量的话会从活动对象中寻找函数

     

     

    再见一些例子:

     1 <script type="text/javascript">
     2 
     3 /*在预编译过程中func是window环境下的活动对象中的一个属性,值是一个函数,覆盖了undefined值*/
     4 
     5 alert(func); //function func(){alert("hello!")}
     6 
     7 var func = "this is a variable"
     8 
     9 function func(){
    10 
    11 alert("hello!")
    12 
    13 }
    14 
    15 /*在执行过程中遇到了var重新赋值为"this is a variable"*/
    16 
    17 alert(func);  //this is a variable
    18 
    19 </script>

    (此例子也是来自于参考2)

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

     


     

    按块执行JavaScript代码

    所谓代码块就是使用<script>标签分隔的代码段。例如,下面两个<script>标签分别代表两个JavaScript代码块。

     

    <script>
    
    // JavaScript代码块1
    
    var a =1;
    
    </script>
    
    <script>
    
    // JavaScript代码块2
    
    function f(){
    
        alert(1);
    
    }
    
    </script>

     

     

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

    由于JavaScript是按块执行的,所以如果在一个JavaScript块中调用后面块中声明的变量或函数就会提示语法错误。例如,当JavaScript解释器执行下面代码时就会提示语法错误,显示变量a未定义,对象f找不到。

     

    <script>
    
    // JavaScript代码块1
    
    alert(a);
    
    f();
    
    </script>
    
    <script>
    
    // JavaScript代码块2
    
    var a =1;
    
    function f(){
    
        alert(1);
    
    }
    
    </script>

     

     

    虽然说,JavaScript是按块执行的,但是不同块都属于同一个全局作用域,也就是说,块之间的变量和函数是可以共享的。

     

     


     

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

    由于JavaScript是按块处理代码,同时又遵循HTML文档流的解析顺序,所以在上面示例中会看到这样的语法错误。但是当文档流加载完毕,如果再次访问就不会出现这样的错误。例如,把访问第2块代码中的变量和函数的代码放在页面初始化事件函数中,就不会出现语法错误了。

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

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

    注意如果在一个页面中存在多个windows.onload事件处理函数,则只有最后一个才是有效的,为了解决这个问题,可以把所有脚本或调用函数都放在同一个onload事件处理函数中,例如:

    window.onload = function(){

        f1();

        f2();

        f3();

    }

    (关于怎样在文件中执行多个window.onload函数可以参见博客中的window.onload函数)

    而且通过这种方式可以改变函数的执行顺序,方法是:简单地调整onload事件处理函数中调用函数的排列顺序。

    除了页面初始化事件外,我们还可以通过各种交互事件来改变JavaScript代码的执行顺序,如鼠标事件、键盘事件及时钟触发器等方法。

     

     

     


     

    JavaScript输出脚本的执行顺序

    在JavaScript开发中,经常会使用document对象的write()方法输出JavaScript脚本。那么这些动态输出的脚本是如何执行的呢?例如:

    document.write('<script type="text/javascript">');

    document.write('f();');

    document.write('function f(){');

    document.write('alert(1);');

    document.write('}');

    document.write('</script>');

    运行上面代码,我们会发现:document.write()方法先把输出的脚本字符串写入到脚本所在的文档位置,浏览器在解析完document.write()所在文档内容后,继续解析document.write()输出的内容,然后才按顺序解析后面的HTML文档。

    也就是说,JavaScript脚本输出的代码字符串会在输出后马上被执行。

    请注意,使用document.write()方法输出的JavaScript脚本字符串必须放在同时被输出的<script>标签中,否则JavaScript解释器因为不能够识别这些合法的JavaScript代码,而作为普通的字符串显示在页面文档中。例如,下面的代码就会把JavaScript代码显示出来,而不是执行它。

    document.write('f();');

    document.write('function f(){');

    document.write('alert(1);');

    document.write(');');

    但是,通过document.write()方法输出脚本并执行也存在一定的风险,因为不同JavaScript引擎对其执行顺序不同,同时不同浏览器在解析时也会出现Bug。

    Ø 问题一,找不到通过document.write()方法导入的外部JavaScript文件中声明的变量或函数。例如,看下面示例代码。

    document.write('<script type="text/javascript" src="test.js">

    </script>');

    document.write('<script type="text/javascript">');

    document.write('alert(n);');  // IE提示找不到变量n

    document.write('</script>');

    alert(n+1);                          // 所有浏览器都会提示找不到变量n

    外部JavaScript文件(test.js)的代码如下:

    var n = 1;

    分别在不同浏览器中进行测试,会发现提示语法错误,找不到变量n。也就是说,如果在JavaScript代码块中访问本代码块中使用document.write()方法输出的脚本中导入的外部JavaScript文件所包含的变量,会显示语法错误。同时,如果在IE浏览器中,不仅在脚本中,而且在输出的脚本中也会提示找不到输出的导入外部JavaScript文件的变量(表述有点长和绕,不懂的读者可以尝试运行上面代码即可明白)。

    Ø 问题二,不同JavaScript引擎对输出的外部导入脚本的执行顺序略有不同。例如,看下面示例代码。

    <script type="text/javascript">

    document.write('<script type="text/javascript" src="test1.js">

    </script>');

    document.write('<script type="text/javascript">');

    document.write('alert(2);')

    document.write('alert(n+2);');

    document.write('</script>');

    </script>

    <script type="text/javascript">

    alert(n+3);

    </script>

    外部JavaScript文件(test1.js)的代码如下所示。

    var n = 1;

    alert(n);

    在IE浏览器中的执行顺序如图1-6所示。

     

    图1-6  IE 7浏览器的执行顺序和提示的语法错误

    在符合DOM标准的浏览器中的执行顺序与IE浏览器不同,且没有语法错误,如图1-7所示的是在Firefox 3.0浏览器中的执行顺序。

     

    图1-7  Firefox 3浏览器的执行顺序和提示的语法错误

    解决不同浏览器存在的不同执行顺序,以及可能存在Bug。我们可以把凡是使用输出脚本导入的外部文件,都放在独立的代码块中,这样根据上面介绍的JavaScript代码块执行顺序,就可以避免这个问题。例如,针对上面示例,可以这样设计:

    <script type="text/javascript">

    document.write('<script type="text/javascript" src="test1.js"></script>');

    </script>

    <script type="text/javascript">

    document.write('<script type="text/javascript">');

    document.write('alert(2);') ; // 提示2

    document.write('alert(n+2);'); // 提示3

    document.write('</script>');

    alert(n+3); // 提示4

    </script>

    <script type="text/javascript">

    alert(n+4); // 提示5

    </script>

    这样在不同浏览器中都能够按顺序执行上面代码,且输出顺序都是1、2、3、4和5。存在问题的原因是:输出导入的脚本与当前JavaScript代码块之间的矛盾。如果单独输出就不会发生冲突了。

     

     

     

     

     

     

  • 相关阅读:
    python中的编码问题
    CVPR2018 Tutorial 之 Visual Recognition and Beyond
    hdu 1376 Octal Fractions
    hdu 1329 Hanoi Tower Troubles Again!
    hdu 1309 Loansome Car Buyer
    hdu 1333 Smith Numbers
    hdu 1288 Hat's Tea
    hdu 1284 钱币兑换问题
    hdu 1275 两车追及或相遇问题
    hdu 1270 小希的数表
  • 原文地址:https://www.cnblogs.com/CBDoctor/p/3745246.html
Copyright © 2011-2022 走看看