JavaScript执行顺序
JavaScript执行顺序
JavaScript(以下简称js) 是一种描述型脚本语言,由浏览器进行动态的解析与执行。由于js编写位置比较灵活,所以处在不同位置的js代码执行顺序也是不同的。js和其他编程语言相比比较随意,所以js代码中充满各种奇葩的写法。苦逼的公司造就苦逼程序员全栈工程师,工作中总是经常自己写些简单的前端页面,经常被js的执行顺序袭扰,索性找个大块儿时间好好学习总结下,理解各型各色的写法,希望能对js的语言特性有深入的理解。
函数的声明和调用
函数的定义方式大体有以下两种,浏览器对于不同的方式有不同的解析顺序。
- 使用
function
关键字声明一个函数,再指定一个函数名,叫函数声明。1
2
3function Fn1(){
alert("Hello World!");
} - 使用
function
关键字声明一个函数,但未给函数命名,最后将匿名函数赋予一个变量,叫函数表达式,这是最常见的函数表达式语法形式。如果遇到函数声明,则进行预处理(类似C语言的编译);如果遇到函数表达式,则只是将函数赋值给一个变量,不进行预处理,待调用的时候再进行处理。1
2
3var Fn2 = function(){
alert("Hello World!");
}
@(#函数声明)👆↑ 这段代码()可以正常运行,弹出”Hello World!”警告对话框。浏览器对Fn1()进行了预处理,再从Fn1();开始运行。1
2
3
4Fn1();
function Fn1(){
alert("Hello World!");
}
再如:
@(#函数表达式)
1 | Fn2(); |
👆↑ 这段代码会在Chrome浏览器报错,DevTools会提示 Uncaught TypeError: Fn2 is not a function
。浏览器未对Fn2进行预处理,依序执行,所以报错Fn2不是函数。
js代码块及引用的外部js文件的处理
这里js代码块指的是一对<script type="text/javascript" charset="utf-8">some js code</script>
标签中间包裹着的js代码;外部引用的文件指的是<script src="/javascripts/application.js" type="text/javascript" charset="utf-8" async defer></script>
中src属性中路径下的js文件中的代码。
页面加载过程中,浏览器会对每个js代码块或文件进行独立扫描。所以在一个块或文件中,函数可以先调用,再进行函数声明(#函数声明
);但两个块中,函数声明所在的块必须在函数调用所在的块之前。略绕,,看例子👇↓
1 | <script type="text/javascript"> |
需要注意的是,外部引用的js文件也存在这种情况,,js文件中定义的变量和函数只能在之后引用。即js.js文件中以函数声明的方式定义了fn..👇↓
1 | <script type='text/javascript'> |
重复定义函数会覆盖前面的定义
这和变量的重复定义是一样的,,👇↓
1 | function fn(){ |
但如果是这样呢,,👇↓
1 | fn(); |
body标签内的onload属性与body标签之间函数的执行
body内部的函数会先于onload的函数执行,测试代码:👇↓
1 | //html head... |
body
的onload
事件触发条件是body
内容加载完成,,所以在加载body
内容的过程中执行了<script>
标签中的alert("I am inside the Wall..");
。至此body
标签中的内容加载完成,触发body
标签中onload
函数,执行fnOnLoad
方法。
script标签放在什么位置
scripts会引发阻塞并行下载的问题。HTTP/1.1 specification建议每次不要同时从一个hostname下载超过两个的组件。如果你把网站上引用的图片放在不同hostname上,那么每次加载该网页时,可以下载同时两个以上的图片资源。然而,当引用的外部js文件正在下载时,无论其他资源是否与该js文件处于同一hostnamme,都不能(并行)下载。所以yahoo建议将script放在尾部,这样能加速网页加载。^1
将script放在尾部的缺点,是浏览器只能先解析完整个HTML页面,再下载JS。而对于一些高度依赖于JS的网页,就会显得慢了。所以将script放在尾部也不是最优解,最优解是一边解析页面,一边下载JS。
所以,出现了一种更modern的方式:使用async和defer。80%的现代浏览器都认识async和defer属性[3],这两个属性能让浏览器做到一边下载JS(还是只能同时下载两个JS),一边解析HTML。他的优点不是增加JS的并发下载数量,而是做到下载时不block解析HTML。^2
1 | <script type="text/javascript" src="path/to/script1.js" async></script> |
带async
属性的script会异步执行,只要下载完就执行,这会导致script2.js可能先于script1.js执行(如果script2.js比较大,下载慢)。defer
就能保证script有序执行,script1.js先执行,script2.js后执行。
参考链接:
http://www.jb51.net/article/36755.htm
https://developers.google.com/speed/docs/insights/BlockingJS
https://developers.google.com/speed/docs/insights/OptimizeCSSDelivery
https://developers.google.com/speed/docs/insights/PrioritizeVisibleContent