JavaScript高性能编写技巧

前言

这段时间把《高性能JavaScript》书籍读完,受益良多。在读书的过程把重点精简地提炼并结合自己的经验记录下来。也希望看完这篇文章能够给对JavaScript多了解一点点。

目录

  • 加载和执行
  • 数据存取
  • DOM编程
  • 算法和流程控制
  • 快速响应的用户界面
  • Ajax
  • 编程实战
  • 构建并部署高性能的JavaScript应用

1. 加载和执行

了解: 当浏览器在执行JavaScript代码时,是不能够同时做其他事情。因为,绝大多数的浏览器使用单一线程来处理UI和JS。简单地说,

1.1 脚本的位置

<html>
	<head>
		<script 1></script>
		<script 2></script>
		<script 3></script>
	</head>
	<body>
		<div></div>
	</body>
</html>

这样的代码存在十分严重的性能问题,整个解析的过程会卡顿在<script 1,2,3>的下载和执行的过程。而页面内容迟迟不能呈现在用户面前。
记住: 浏览器在解析n之前不会渲染页面的任何部分,此时的表现为空白。所以 < script > 放在顶部的做法非常不可取。

js文件的下载执行流程::
在这里插入图片描述
那么该如何去改进?
!!!并行下载js (后来的IE8、FireFox3.5、Safari4、Chrome2都允许并行下载JS文件了,也就是说你现在接触到的大多浏览器的JS文件的下载都是并行的)
也就是:::
在这里插入图片描述
JS文件是能够并行下载,但是页面还是会阻塞其他资源下载(如图片)并且会等待所有的JS文件下载完成并执行完成。
因此强烈推荐将所有的< script >标签尽可能的防盗标签的底部,已尽量减少对整个页面的影响。

1.2 组织脚本

考虑到Http请求会有额外的开销,所以script标签的个数不能过多。这个时候就要考虑将js文件进行合并已减少数量了。不过现在的很多打包工具都有这样的功能,所以不用太过关注这个问题。

1.3无阻塞脚本

减少JS文件的大小及数量是优化的第一步(毕竟效果有限,因为js总会越来越多,越来越大)所以要考虑无阻塞的加载脚本方式。
无阻塞脚本的秘诀在于:页面加载完成后再就在JS代码(即在window.load()方法出发后再下载)

1.4延迟的脚本

Defer属性:
HTML4为< script >标签定义了defer扩展属性,该属性指明的JS文件不会修改DOM。但一开始只有IE4、FireFox3.5+支持;不过后来已经被所有的主流浏览器所支持。后来的H5中还添加了async扩展属性。
区别

defer async
并行下载JS 并行下载JS
等待页面完成后执行(但是在load()方法调用之前) 下载完成后执行

1.5动态脚本

< script >标签跟其他的元素一样,都能通过DOM操作。

let scriptaEle = document.createElement('script');
scriptEle.type = 'text/script';
scriptEle.src = 'file1.js';
document.getElementByTagName('head')[0].appendChild(scriptEle);

这个技术的重点在于:无论何时启动下载,文件的下载和执行的过程不会阻塞页面的其他过程。

温馨提示:

  • 新创建的JS添加到< head >中会更好,因为在< body >中IE中可能会抛出“操作已终止”的信息
  • 下载的JS通常会立即执行,除了opera和firefox会等待此前所有的动态脚本节点执行完毕)

有时该JS会被其他的JS调用其中的方法,所以有时需要监听JS下载的状态。

  • firefox、opera、chrome、safari3以上 | 完成时会触发load()函数
  • IE为< script >提供readystate属性,但并不是其中的所有状态都会执行(通常loaded/complete出现一种就可以)
所有状态 Value
uninitialized 初始
loading 开始下载
loaded 下载完成
interactive 完成但未可用
complete 已准备就绪

动态脚本凭借其在跨浏览其兼容性和易用的优势,成为最通用的无阻加载解决方案
记住哈,想要优化JS的下载就采用动态脚本技术!!!

1.6 XMLHttpRequest脚本注入

**了解:**另一种无阻塞加载脚本方式:使用XMLHttpRequest技术获取脚本并注入页面(也就是用XHR网络请求JS然后注入到页面中)
例如:

let xhr = new XMLHttpRequest();
xhr.open('get','file.js',true);
xhr.onReadyStateChange = function () {
	if(xhr.readyState === 4) {\
		if(xhr.state >= 200 && xhr.status < 300 || ==304) {
			//请求JS成功后,创建script标签,将JS内容赋给script标签;然后嵌入页面 
			let script = document.createElement('script');
			script.type = 'text/javascript';
			script.text = xhr.responseText;
			document.body.appendChild(script);
		}
	}
}
// 发起请求
xhr.send();

优点:

  • JS不会立即执行,可以放到你准备好的时候再执行都可以(灵活控制JS的执行时间)
  • 所有主浏览器都支持

1.7 推荐的无阻塞模式

向页面中添加大量JS的推荐做法:
第一步:先添加动态加载所有的代码
第二部:再加载剩余JS代码

有一些如LazyLoad的类库可以协助我们快速的使用无阻塞加载JS

2. 数据存取

了解: 计算机科学中有一个经典的问题:通过改变数据的存取位置来获得最佳的读写性能。数据的存取位置关系到代码执行过程中的检索速度。

js中有四种基本的数据存取位置:
字面量
: 字面量只代表本身,不存储在特定位置。
: 有:字符串、数字、布尔、数组、函数、正则表达式、nullull、undefined
本地变量
: 开发人员使用var、let等定义的数据存储单元
数组元素
: 存储在JS数组对象内部,以数字为索引
对象成员
: 存储在JS对象内部,以字符串为索引

2.1 管理作用域

**了解:**作用域是理解JS的关键,所以要重点的搞明白这一部分,从性能和功能的角度去思考。

  • 确定哪些变量能被访问
  • 确定this
  • 关系到性能

需要了解以上问题的原理!

2.2 作用域链和标志符解析

**了解:**JS函数是Function对象,和普通对象一样拥有

  • 可编程访问的属性

  • 不可通过代码访问的内部属性(而这其中有着非常重要的 [scope] )
    在这里插入图片描述
    函数执行时创建一个为执行环境的内部对象。函数每次执行的都会创建独一无二的执行环境。当函数执行完毕,执行环境就会被销毁。
    每个执行环境都有自己的作用域链,用于解析标志符,在函数执行的过程中,每遇到一个变量,都会经历标志符解析的过程以决定从哪里获取存储数据。从顶层的“活动对象”作用域开始便利作用域链,知道找到为止。
    正式这个搜索过程,影响性能

    2.2 标志符解析的性能

    一个标志符所在的位置越深,他的读写速度越慢;所以全局变量访问速度越慢(因为他总是在作用域链的末端)
    (这也是链式结构的特点)
    综上述:应该尽量多的访问局部变量。
    经验法则:如果某个跨作用域的值在函数中被引用一次以上,则把他存储到局部变量

    2.3 改变作用域链

    有两个语句可以在执行时,临时改变作用域链。

    第一个:with

    • 类似功能通常用来避免书写重复代码。
    • 给对象的所有属性创建了一个变量。
    • 并且把该对象添加到作用域链顶,访问该属性速度变快,但是访问其他的变慢
    • 一般来说如果多次访问document,可以吧document付给局部变量即可大大提升性能
    • with应该尽量避免使用

· 第二个:try…catch

  • 当进入catch时,会将异常对象推入作用域链的顶部

    2.4 动态作用域

    with、try…catch、eval()都被认为是动态作用域链,动态作用域只存在于代码执行过程中,因此无法通过静态分析。

    2.5 闭包、作用域和内存

    闭包:js最强的特性之一,函数访问局部作用域之外的数据,使用闭包可能会导致性能问题,因为闭包函数阻碍了函数被正常回收,因为闭包有自己的作用域链,并且指向跟函数的作用域链一样。
    当闭包代码执行时,会创建一个执行环境。
    在这里插入图片描述
    闭包代码中访问的属性的位置不在第一层,这就是使用闭包最需关注的性能点;在频繁访问跨作用域的标志符时,每次都有性能损失。(闭包同时关系到内存和执行速度)

    2.6 对象成员

    访问成员比访问字面量或变量要慢,为了理解其中的原因,有必要先了解JS的对象本质。

    2.7 原型

    js的对象基于原型,他定义并实现了一个新创建的对象,所必须包含的成员列表。
    对象通过一个内部属性帮到他的原型,在firefox、safari、chrome浏览器中,这个属性_proto_对开发者可见,而其他浏览器确不允许脚本访问此属性。

    小结

    • 在JS中,数据存储的位置对代码整体性能产生重大的影响。
    • 访问字面量和局部变量速度最快,相反,访问数组元素和对象成员相对较慢
    • 由于局部变量在链顶位置,所以更快
    • 全局变量在链尾位置,所以更慢
    • 减少嵌套对象
    • 通常来说,把常用的对象成员,数组元素,跨域变量保存到局部变量中可改变JS的性能
    • 多用局部变量

不知不觉写到这里已经挺长的了,所以决定留到下一篇文章吧。

  • 加载和执行
  • 数据存取
  • DOM编程
  • 算法和流程控制
  • 快速响应的用户界面
  • Ajax
  • 编程实战
  • 构建并部署高性能的JavaScript应用
©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值