阅读更多

23顶
3踩

Web前端

转载新闻 JavaScript 装载和执行

2013-06-05 11:02 by 副主编 WnouM 评论(8) 有10431人浏览


本文转载自酷壳网原文内容如下:

一两个月前在淘宝内网里看到一个优化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放在网页的最后面了。因为,绝大多数的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文件下载到用户的本地,但是不执行,仅当在我们想要执行的时候去执行。

要是我们有下面这样的试就好了:

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标签,于是我们有下面这样的代码:

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+Shift+I,切换到network页,你就可以看到下载了alert.js但是没有执行,然后我们再用示例五的方式,因为浏览器端有缓存了,不会再多服务器上下载alert.js了。所以,就能保证执行速度了。

关于这种preload你应该不会陌生了。你还可以使用Ajax的方式,如:

var xhr = new XMLHttpRequest();
xhr.open('GET', 'new.js');
xhr.send('');


我这里不就再多说了。好了,这是所有的内容了,希望大家看过后能对Javascript的载入和执行,以及相关的技术有个了解。同时,也希望各前端高手不吝 赐教!
  • 大小: 129.7 KB
来自: 酷壳
23
3
评论 共 8 条 请登录后发表评论
8 楼 leon1509 2013-06-08 09:01
写得真不错,就是有些地方语句不通顺!
7 楼 leaow567 2013-06-07 09:49
好文,受教了
6 楼 lnaigg 2013-06-07 00:20
xhr+eval还有个问题是无法跨域
5 楼 lnaigg 2013-06-07 00:19
看了半天,示例六不是异步装载、异步执行啊
cachejs函数只是让浏览器记录一下有这么个脚本,其实是没有下载下来的
loadjs函数才是真正下载脚本。
而且起到缓存作用的是loadjs里面的script标签,跟cachejs里面创建的对象没虾米关系

我的需求是,先异步下载下来,我想执行的时候就执行。
xhr+eval,是可以做到预加载和缓存脚本的。
xhr+eval的问题是,当我想同时load多个脚本文件时,要不低效地串行加载js,要不并行加载js就无法控制先后执行顺序。
4 楼 nomandia 2013-06-06 17:16
如果想阻止异步装在的JS运行可以这么干:
var script = document.createElement('script');
script.defer=true; //注意这个属性
script.src=target_url;

即,script标记的defer属性默认是false的也就是不阻止脚本在装在完毕后运行,把他给禁止了就完了。

其他的倒是没什么,不过基本上不用document.write。
3 楼 天朗java 2013-06-06 09:45
怎么还有人点踩,应该是高人吧,能给点分享不?
2 楼 三里小龙 2013-06-05 12:53
类似缓存的做法的确很值得借鉴,这样基本上达到我们人为去控制脚本的触发,这一点提供了很好的思路,也比较新颖,谢啦!
1 楼 kevinwqw 2013-06-05 12:02
好文,受教了

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • 计算机思科配置个人总结,有关思科模拟器实训总结范文

    有关思科模拟器实训总结范文篇一:Cisco路由模拟器实验总汇实验一:1. 口令和设备名设置添加任意的交换机或路由器,先对...进入全局配置模式switch(config)#hostname swa ;设置交换机名swa(config)#enable secret ...

  • 黑马点评项目全部功能实现及详细笔记--Redis练手项目

    黑马点评项目实战篇全部业务实现以及部分代码逻辑分析与记录

  • LVS的DR模式(ldirectord的使用,keepalived配置高可用集群)

    vim /etc/yum.repos.d/rhel-source.repo 添加如下几行 [rhel-source] name=Red Hat Enterprise Linux $releasever - $basearch - Source baseurl=http://172.25.65.250/rhel6.5 enabled=1 gpgcheck=0 [High...

  • 二分类器的常用评价指标

    虽说评价指标之间却有全面和局部、对数据不平衡敏感和不敏感之别,但是希望诸位读者和我自己都不要孤立和教条地看待和选择指标,不同的业务场景、不同的目的有不同的要求,运用智慧和经验灵活运用才是我们所追求的...

  • jboss怎么配置支持php,Eclipse+JBoss+MySQL开发环境设置全攻略_php

    作为开发环境,如果采用经典配置:JBuilder+weblogic+oracle,自是得心应手,但价格是惊人的。此配置主要是针对大型或超大型应用,硬件要求也很高,针对国内以中小型应用为主的现况,不作推荐。虽然国内开发者早已...

  • NDSL

    很好,请各位继续往下看。2008年idsl和NDSL的价格稳定在1100左右. 【为何选择NDSL?】 有这种疑问的朋友大多数是没有看到NDSL实机的,倘若真正是看到了NDSL实机,只要你是任天堂爱好者,砸锅卖铁...

  • 水清则无鱼--走出软件作坊:三五个人十来条枪 如何成为开发正规军(八)

    那天,突然给我打了电话,说要请我吃饭。 饭肯定是不能白吃的。朋友告诉我:唉,烦心啊。客户不成熟,是麻烦事。客户太成熟,也是个麻烦事。 我说,此话怎讲? 我朋友说:你看,我过去跟单,客户对软件不懂...

  • 转:水清则无鱼--走出软件作坊:三五个人十来条枪 如何成为开发正规军(八)...

    那天,突然给我打了电话,说要请我吃饭。 饭肯定是不能白吃的。朋友告诉我:唉,烦心啊。客户不成熟,是麻烦事。客户太成熟,也是个麻烦事。 我说,此话怎讲? 我朋友说:你看,我过...

  • 三五个人十来条枪 如何成为开发正规军(八)

    那天,突然给我打了电话,说要请我吃饭。 饭肯定是不能白吃的。朋友告诉我:唉,烦心啊。客户不成熟,是麻烦事。客户太成熟,也是个麻烦事。 我说,此话怎讲? 我朋友说:你看,我过去跟单,客户对软件...

  • 玩转MySQL:一站式解决分库分表后患问题方案

    当然,保障分布式系统下ID唯一性的解决方案很多,如下: ①通过设置数据库自增机制的起始值和步长,来控制不同节点的ID交叉增长,保证唯一性。 ②在业务系统中,利用特殊算法生成有序的分布式ID,比如雪花算法、...

  • Redis学习笔记①基础篇_Redis快速入门

    配置完成之后就可以启动 redis 了(此时是按照配置文件来运行的) redis-server /usr/local/src/redis-6.2.6/redis.conf 可以使用如下命令查看 redis 服务是否正常运行 ps -ef | grep redis 关闭 redis 服务的话,...

  • 九月十月百度人搜,阿里巴巴,腾讯华为小米搜狗笔试面试八十题

    ,你可以把你的答案或代码直接评论在本文之下,也可以通过私信或邮件发给我,感谢诸位。同时,以下所有任何题目所给的点评里的答案,尤其是所给的外部链接若有任何问题,欢迎在本文评论下留言指正,谢谢。 答题除了...

  • 使用循环神经网络(RNN)实现影评情感分类

    七、对模型进行配置 模型的配置参数大多数都被提取出来,单独放到了settings.py文件中,可以在这里对模型进行配置。 八、运行模型 至此,模型构建完成。模型的运行步骤大致如下: 1.确保数据文件放在了对应路径中,...

  • 关于使用Spring导致c3p0数据库死锁问题

    只要你用Spring控制事务,写了如下的事务控制配置: id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"&gt;   ref="transactionManager"/&gt; ...

  • 学习笔记:SpringCloud 微服务技术栈_实用篇①_基础知识

    若文章内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系博主删除。 前言 学习视频链接 SpringCloud + RabbitMQ + Docker + Redis + 搜索 + 分布式,史上最全面的 SpringCloud 微服务...

  • Java软件开发实战 Java基础与案例开发详解 19-7 综合示例 共10页.pdf

    完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

  • 100款古风PPT (60)(1).pptx

    【ppt素材】工作总结、商业计划书、述职报告、读书分享、家长会、主题班会、端午节、期末、夏至、中国风、卡通、小清新、岗位竞聘、公司介绍、读书分享、安全教育、文明礼仪、儿童故事、绘本、防溺水、夏季安全、科技风、商务、炫酷、企业培训、自我介绍、产品介绍、师德师风、班主任培训、神话故事、巴黎奥运会、世界献血者日、防范非法集资、3D快闪、毛玻璃、人工智能等等各种样式的ppt素材风格。 设计模板、图片素材、PPT模板、视频素材、办公文档、小报模板、表格模板、音效配乐、字体库。 广告设计:海报,易拉宝,展板,宣传单,宣传栏,画册,邀请函,优惠券,贺卡,文化墙,标语,制度,名片,舞台背景,广告牌,证书,明信片,菜单,折页,封面,节目单,门头,美陈,拱门,展架等。 电商设计:主图,直通车,详情页,PC端首页,移动端首页,钻展,优惠券,促销标签,店招,店铺公告等。 图片素材:PNG素材,背景素材,矢量素材,插画,元素,艺术字,UI设计等。 视频素材:AE模板,会声会影,PR模板,视频背景,实拍短片,音效配乐。 办公文档:工作汇报,毕业答辩,企业介绍,总结计划,教学课件,求职简历等PPT/WORD模板。

  • DS18B20 - 数字温度传感器

    DS18B20是一种数字温度传感器,被广泛应用于各种温度监测和控制系统中。它基于单总线协议,具有数字输出和高精度的温度测量能力。以下是对DS18B20的详细介绍: DS18B20采用了数字温度传感器技术,可以准确测量环境温度并将结果以数字形式输出。它的工作原理基于温度导致的半导体材料电阻值的变化。DS18B20内部集成了温度传感器、模数转换器和单总线接口电路,使得它在一个小型封装中实现了高度集成和功能完整性。 DS18B20的特点之一是其数字输出和单总线接口。它使用单总线协议进行通信,只需要一条数据线连接到主控设备,简化了连接和布线。通过发送特定的指令和接收传感器的响应,主控设备可以读取温度值并进行相应的处理和控制操作。 DS18B20具有高精度的温度测量能力。它可以在范围从-55°C到+125°C内测量温度,并具有0.5°C的温度分辨率。此外,它还具有温度报警功能,可以在温度达到预设阈值时触发警报,提供温度监测和控制的实时反馈。 DS18B20在各种应用中都得到了广泛应用。它适用于温度监测和控制系统,如室内温度监测、恒温控制、温度报警系统等。它还可以用于工业自动化、气象站、电

  • si2302中文资料-数据手册-参数.pdf

    对MOS的应用和参数做了详细的说明附带测试数据

  • 光能手机万能充电器设计.doc

    用TMS320F28027的DSP作为主控芯片,制作了一部太阳能充电和和逆变装置。设计主要采用了DC-DC变换器,推挽变换器,DC-AC变换器,通过算法寻找太阳能电池的最大功率点。 关键词:DSP、充电、buck电路、最大功率点跟踪、登山法

Global site tag (gtag.js) - Google Analytics