一两个月前在淘宝内网里看到一个优化Javascript代码的竞赛,发现有不少的人对Javascript的执行和装载的基础并不懂,所以,从那天起我就想写一篇文章,但一直耽搁了。自上篇《浏览器渲染原理简介》,正好也可以承前启后。
首先,我想说一下Javascript的装载和执行。通常来说,浏览器对于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>标签会阻塞后续资源的载入以及整个页面的生成。我专门做了一个示例你可以看看:示例一。 注意:我的alert.js中只有一句话:alert(“hello world”) ,这更容易让你看到javascript是怎么阻塞后面的东西的。
所以,你知道为什么有很多网站把javascript放在网页的最后面了,要么就是动用了window.onload或是docmuemt ready之类的事件。
另外,因为绝大多数的Javascript代码并不需要等页面,所以,我们异步载入的功能。那么我们怎么异步载入呢?
document.write方式
于是,你可能以为document.write()这种方式能够解决不阻塞的方式。你当然会觉得,document.write了 的<script>标签后就可以执行后面的东西去了,这没错。对于在同一个script标签里的Javascript的代码来说,是这样的, 但是对于整个页面来说,这个还是会阻塞。 下面是一段测试代码:
- <script type="text/javascript" language="javascript">
- function loadjs(script_filename) {
- document.write('<' + 'script language="javascript" type="text/javascript"');
- document.write(' src="' + script_filename + '">');
- document.write('<'+'/script'+'>');
- alert("loadjs() exit...");
- }
- var script = 'http://coolshell.cn/asyncjs/alert.js';
- loadjs(script);
- alert("loadjs() finished!");
- </script>
- <script type="text/javascript" language="javascript">
- alert("another block");
- </script>
你觉得alert的顺序是什么?你可以在不同的浏览器里试一试。这里的想关的测试页面:示例二。
script的defer和async属性
IE自从IE6就支持defer标签,如:
- <script defer type="text/javascript" src="./alert.js" > </script>
对 于IE来说,这个标签会让IE并行下载js文件,并且把其执行hold到了整个DOM装载完毕(DOMContentLoaded),多个defer 的<script>在执行时也会按照其出现的顺序来运行。最重要的是<script>被加上defer后,其不会阻塞后续DOM 的的渲染。但是因为这个defer只是IE专用,所以一般用得比较少。
而我们标准的的HTML5也加入了一个异步载入javascript的属性:async,无论你对它赋什么样的值,只要它出现,它就开始异步加载js文 件。但是, async的异步加载会有一个比较严重的问题,那就是它忠实地践行着“载入后马上执行”这条军规,所以,虽然它并不阻塞页面的渲染,但是你也无法控制他执 行的次序和时机。你可以看看这个示例去感受一下。
支持 async标签的浏览器是:Firefox3.6+,Chrome 8.0+,Safari 5.0+,IE 10+,Opera还不支持(来自这里)所以这个方法也不是太好。因为并不是所有的浏览器你都能行。
动态创建DOM方式
这种方式可能是用得最多的了。
- function loadjs(script_filename) {
- var script = document.createElement('script');
- script.setAttribute('type', 'text/javascript');
- script.setAttribute('src', script_filename);
- script.setAttribute('id', 'coolshell_script_id');
- script_id = document.getElementById('coolshell_script_id');
- if(script_id){
- document.getElementsByTagName('head')[0].removeChild(script_id);
- }
- document.getElementsByTagName('head')[0].appendChild(script);
- }
- var script = 'http://coolshell.cn/asyncjs/alert.js';
- loadjs(script);
这个方式几乎成了标准的异步载入js文件的方式,这个方式的演示请参看:示例三。 这方式还被玩出了JSONP的东东,也就是我可以为script的src指定某个后台的脚本(如PHP),而这个PHP返回一个javascript函 数,其参数是一个json的字符串,返回来调用我们的预先定义好的javascript的函数。你可以看一下这个示例:t.js (这个示例是我之前在微博征集的一个异步ajax调用的小例子)
按需异步载入js
上面那个DOM方式的例子解决了异步载入Javascript的问题,但是没有解决我们想让他按我们指定的时机运行的问题。所以,我们只需要把上面那个DOM方式绑到某个事件上来就可以了。
比如:
绑在window.load事件上——示例四
你一定要比较一下示例四和示例三在执行上有什么不同,我在这两个示例中都专门用了个代码高亮的javascript,看看那个代码高亮的的脚本的执行和我的alert.js的执行的情况,你就知道不同了)
- window.load = loadjs("http://coolshell.cn/asyncjs/alert.js")
绑在特定的事件上——示例五
- <p style="cursor: pointer" onclick="LoadJS()">Click to load alert.js </p>
这个示例很简单了。当你点击某个DOM元素,才会真正载入我们的alert.js。
更多
但是,绑定在某个特定事件上这个事似乎又过了一点,因为只有在点击的时候才会去真正的下载js,这又会太慢了了。好了,到这里,要抛出我们的终极问题——我们想要异步地把js文件下载到用户的本地,但是不执行,仅当在我们想要执行的时候去执行。
要是我们有下面这样的方式就好了:
- var script = document.createElement("script");
- script.noexecute = true;
- script.src = "alert.js";
- document.body.appendChild(script); //后面我们可以这么干 script.execute();
可惜的是,这只是一个美丽的梦想,今天我们的Javascript还比较原始,这个“JS梦”还没有实现呢。
所以,我们的程序员只能使用hack的方式来搞。
有的程序员使用了非标准的script的type来cache javascript。如:
- <script type=cache/script src="./alert.js"></script>
因 为”cache/script”,这个东西根本就不能被浏览器解析,所以浏览器也就不能把alert.js当javascript去执行,但是他又要去下 载js文件,所以就可以搞定了。可惜的是,webkit严格符从了HTML的标准——对于这种不认识的东西,直接删除,什么也不干。于是,我们的梦又破 了。
所以,我们需要再hack一下,就像N多年前玩preload图片那样,我们可以动用object标签(也可以动用iframe标签),于是我们有下面这样的代码:
- function cachejs(script_filename){
- var cache = document.createElement('object');
- cache.data = script_filename;
- cache.id = "coolshell_script_cache_id";
- cache.width = 0;
- cache.height = 0;
- document.body.appendChild(cache);
- }
然后,我们在的最后调用一下这个函数。请参看一下相关的示例:示例六
在Chrome下按 Ctrl+Shit+I,切换到network页,你就可以看到下载了alert.js但是没有执行,然后我们再用示例五的方式,因为浏览器端有缓存了,不会再从服务器上下载alert.js了。所以,就能保证执行速度了。
关于这种preload这种东西你应该不会陌生了。你还可以使用Ajax的方式,如:
- var xhr = new XMLHttpRequest(); xhr.open('GET', 'new.js'); xhr.send('');
到这里我就不再多说了,也不给示例了,大家可以自己试试去。
最后再提两个js,一个是ControlJS,一个叫HeadJS,专门用来做异步load javascript文件的。
好了,这是所有的内容了,希望大家看过后能对Javascript的载入和执行,以及相关的技术有个了解。同时,也希望各前端高手不吝赐教!
本文转载自:酷壳网 作者:陈皓
相关推荐
Javascript装载和执行是前端开发中非常重要的知识点,它关系到网页加载效率和用户体验。本文将详细介绍Javascript的装载和执行机制,并通过实例展示在不同情况下的表现。 首先,了解Javascript的装载和执行特性是...
本节课程为初学者提供了 JavaScript 的基本概念、特点、优点和编程技术,旨在帮助学习者快速掌握 JavaScript 的基础知识和编程技能。 一、JavaScript 概述 JavaScript 是一种基于对象、事件驱动的编程语言,由 ...
JavaScript是一种解释性编程语言,其源代码在发往客户端执行之前不需经过编译,而是将文本格式的字符代码发送给客户,由浏览器解释执行。 第四,两种语言所采取的变量是不一样的。Java采用强类型变量检查,即所有...
假设主页面有一个div,里面放置一个iframe 代码如下:”frameBox”> <... height:100% ” frameborder=”0″ scrolling... 3个子页面分别在自己页面加载完[removed]执行 代码如下:function aa(){ var newHeight = docume
JavaScript的代码执行可以分为两个阶段:代码检查装载阶段(预编译阶段)和代码执行阶段。 在代码检查装载阶段,JavaScript引擎会进行变量和函数声明,但不会执行任何赋值操作,所有变量的默认值都是undefined。这...
在探讨JavaScript代码在网页加载过程中的执行顺序时,我们首先需要了解JavaScript在HTML文档中的几种嵌入方式,...总之,正确理解和管理JavaScript在页面加载过程中的执行顺序,有助于编写出更加高效且易维护的代码。
- 介绍了如何使用单步执行和断点来调试代码。 - **3.5.2 监视内存** - 解释了如何监控内存使用情况。 - **3.5.3 追踪问题的源头--查看调用堆栈** - 讲解了如何通过调用堆栈来追踪问题。 - **3.5.4 遇到麻烦了-...
基于CSS和JavaScript的网页选项卡的设计和实现.pdf 本文主要介绍了基于CSS和JavaScript的网页选项卡的设计和实现。该方法通过理论分析和代码结合,实现了网页选项卡的制作。该方法设计的网页选项卡,在页面中已经...
stream.js 是一个很小、完全独立的Javascript类库,它为你提供了一个新的Javascript数据结构:streams.Streams 是一个操作简单的数据结构,很像数组或链接表,但附加了一些非凡的能力。跟数组不一样,streams是一个...
13. 装载器和模块: CommonJS和ES6模块是JavaScript中的两种模块化方案。Node.js使用CommonJS,而浏览器环境通常采用ES6模块。 通过理解并掌握这些JavaScript的基础知识点,开发者可以构建交互式网页和复杂的网络...
源码文件可能包括头文件(.h)、源文件(.cpp)和其他配置文件,这些文件一起构成了MOD装载器的核心功能。 源码分析通常涉及以下几个方面: 1. **模块结构**:首先,我们需要理解代码的模块化结构。每个模块可能...
在无限滚动实现中,jQuery提供了一种简便的方式来绑定滚动事件和执行异步加载新内容的函数。 4. **HTML结构**:`index.html`是网页的结构文件,其中包含了网页的基本元素和布局。在实现微博效果时,HTML可能包含一...
例如,通过获取表单中的 `pageNumber` 值来确定当前页码,然后计算偏移量(offset)和限制(limit),执行 SQL 查询。服务器应返回包含总记录数和当前页数据的 JSON 对象。 在实现分页功能时,通常还需要提供分页...
脚本执行时间主要是浏览器解析和执行JavaScript脚本所需的时间,而浏览器引擎渲染时间涉及呈现和更新页面元素。由于不同的浏览器和版本可能采用不同的渲染技术,性能表现各异,本文主要关注JavaScript脚本执行的优化...
在ASP.NET中,为了提升用户体验,我们经常需要在页面加载时显示进度条,尤其是在处理大量数据或执行耗时操作时。下面将详细讲解如何在ASP.NET中实现页面装载进度条,以及Repeater控件的分页功能。 首先,让我们关注...
异步加载(Asynchronous Loading)是指在不影响主线程执行的情况下,后台加载资源的一种方法。这种方式使得浏览器可以继续处理其他任务,而不是等待单个大文件完全下载完毕。对于大尺寸图像,这意味着在用户实际需要...
除此之外,我们还应该注意到,在同一段脚本中,函数的定义可以出现在其调用之后,这是因为JavaScript的执行机制会先进行变量和函数的声明提升(hoisting)。然而,如果函数定义和调用分布在不同的脚本段里,调用就会...
一个非常简单的非渲染阻塞 javascript 加载器,它将按顺序加载一组库,然后在加载或错误时执行回调函数。 将允许在 、 、 和上获得更高的分数。 它是什么/它是如何工作的 STAN Loader 是一个接受四个参数的函数; ...
用户的浏览器在执行任务时即装载了AJAX引擎。AJAX引擎用JavaScript语言编写,通常藏在一个隐藏的框架中。它负责编译用户界面及与服务器之间的交互。AJAX引擎允许用与应用软件之间的交互过程异步进行,独立于用户与...