高性能javaScript

一、加载执行

1.脚本

1.1 推荐将所有的script标签尽可能放在body标签底部: 由于多数浏览器使用单一进程处理UI界面渲染和脚本执行,所以脚本下载、执行会阻塞其他页面渲染,导致刚开始页面空白,让用户感觉加载时间较长,影响用户体验(*且内嵌脚本不要紧跟在link标签后)

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" type="text/css" href="styles.css">
    <title>Document</title>
</head>
<body>
	<p>hello world!</p>
	<-- 推荐的脚本存放位置 -->
	<script type="type/javascript" src="file1.js"></script>
	<script type="type/javascript" src="file2.js"></script>
	<script type="type/javascript" src="file3.js"></script>
</body>
</html>

1.2 尽量减少script标签的引入

如有多个js依赖文件可以合并成一个,再进行引入
*合并文件可以用离线打包工具或Yahoo!combo handler等实时在线服务实现

1.3 延迟脚本 defer

对应的js文件再页面解析到该标签时开始下载,但不会立即执行,而是dom加载完成(onload事件触发前)再执行。且在带有defer属性的js文件下载时,不会造成阻塞进程,可以与其他类资源并行下载

<script type="type/javascript" src="file1.js" defer></script>

1.4 js动态创建script:

无论何时启动下载,都不会阻塞其他进程 (跨浏览器兼容性)

// IE特有实现方法 ==> 函数封装
function loadScript (url, callback) {
	var script = document.createElement("script")
	script.type = "text/javascript"
	if (script.readyState) { // 判断IE
		script.onreadystatechange = function() {
	 		if (script.readyState == "loaded" || script.readyState == "complete") {
	 			script.onreadystatechange = null
				callback()
			}
		}
	} else { // 其他浏览器
		script.onload = function() {
	 		callback()
		}
	}
	script.src = url
	document.getElementsByTagName("head")[0].appendChild(script)
}

// 调用
loadScript ('file.js', function() {
	alert("loaded!")
	// 无阻塞模式:当调用直接嵌入当前页面时执行下面语句,可避免多产生一次HTTP请求(如使用该语句,需要使用YUI压缩初始化代码)
	// Application.init()
})

1.5 XMLHttpRequest 脚本注入:

使用XHR对象获取js代码,通过创建动态script元素注入到页面 (文件必须同域)

var xhr = new XMLHttpRequest()
xhr.open("get", "file.js", true)
script.onreadystatechange = function() {
	if (script.readyState == 4) {
		if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
			var script = document.createElement("script")
			script.type = "text/javascript"
			script.text= xhr.responseText
			document.body.appendChild(script)
		}
	}
}

1.6 补充

YUI3
LazyLad类库
LABjs

二. 数据存取

三. DOM

四. 算法和流程控制

4.1 循环

基于循环的迭代

  • 标准for循环:for (let i = 0; i < item.length, i++) {}
  • while
  • do-while
  • for-in:避免使用,除非遍历属性数量未知的对象
    *除for-in外,循环模式的选择要基于需求,而非性能

基于函数的迭代

  • forEach

改善循环性能

  1. 减少迭代数量(工作量)

// 把值存储到局部变量:可提速25%,IE中甚至可达到50%

 // item.length一直不变,但是每次循环都要查找,导致性能损耗
for (let i = 0, len = item.length; i < len, i++) {}

// 倒序循环:可提速50% - 60%

// 控制条件与true比较,任何非零数自动转为true, 0等同于false: 等同于每次条件都与0比较
for (let i = item.length; i--) {}
  1. 减少迭代次数:减少循环嵌套

4.2 条件语句

  • if-else :数量较少时,推荐 —> 最可能出现的条件放在首位
  • swich : 数量越多,越推荐
  • 查找表:判断条件较多时,离散数据量大时,更推荐
    eg:
// 将返回值集合存入数组
let results = [result0, result1, result2, result3, result4, result5, result6, result7, result8]
// 返回结果 ---> 通过键值对逻辑映射
return results[value]

esults[value]

4.3 递归

容易出现堆栈溢出,解决方法:改为迭代算法,或使用Memoization避免重复计算

Memoization使用
定义一个封装了基础功能的memoize()函数, eg:

function memoize (fundamental, cache) {
	cache = cache || ()
	let sgell = function (arg) {
		if(!cache.hasOwnProperty(arg)) {
			cache[arg] = fundamental(arg)
		}
		return cache[arg]
	}
	return shell
}
// 调用
// 缓存该函数
let memfactorial = memoize(factorial, {"0": 0, "1": 1}) // factorial()阶乘函数是递归的典型事例
// 调用新函数
let fact5 = memfactorial (5)

五.字符串和正则表达式

5.1 字符串合并的方法

  • str = ‘a’ + ‘b’
  • str = ‘a’; str += ‘b’;
  • array.join() : str = [‘a’, ‘b’].join(‘’)
  • string.concat : str = a; str = str.concat(‘b’ , ‘c’) —> 最灵活的方式,但速度较慢

eg : str += ‘one’ + ‘two’
优化
// 避免产生临时字符串(‘onetwo’),提速 10% - 40%
str += ’one‘
str += ‘two’
等价于
str = ((str + ‘one’) + ‘two’)

5.2 数组项合并(数量较大时,适用于IE7以及更早版本,不考虑IE7及之前版本的话其他版本浏览器不适用会很慢)

解决性能问题

// 该方式IE7随时间和内存消耗以平方关系递增,其他浏览器问题不大,相对普通数组项合并算法有优化
let str = 'I`am a thirty-five character string'
let newStr = ''
let appends = 5000
while (appends--) {
	newStr += str
}

较上面方法性能提升,IE7也可

let str = 'I`am a thirty-five character string'
let strs = []
let newStr = ''
let appends = 5000
while (appends--) {
	strs[str.length] = str
}
newStr = strs.join('')

5.3 正则表达式

回溯:在正则表达式的是实现中回溯是匹配过程的基础组成部分,但是会产生昂贵的计算消耗,一不小心就会失控
回溯失控:当正则表达式导致浏览器假死数秒,淑芬,甚至更久,很可能是因为回溯失控

解决方法:

  1. 具体化:尽可能的具体化分隔符之间的字符串匹配模式, 如:".?" 匹配由双引号包围的字符串,替换为更具体的 [^"\r\n]
  2. 预查和反向引用的模拟原子组
    原子组的写法:(?>…) 省略号表示任意正则表达式的模式
    优化方式:预查的表达式封装在捕获组中并给它添加一个反向引用的方法
    (?=(pattern to make atomic))\1

六.快速响应的用户界面

浏览器UI线程: 用于执行js和更新界面的进程(基于队列)

6.1 使用定时器控制js任务,让出UI线程控制权(最少25ms,不然对ui更新来说不够用)

setTimeout()
setInterval()
*使用定时器序列,同一时间只能有一个定时器,定时器过度使用也会对性能造成负面影响

6.2 分割任务

6.3 Web Workers(新版浏览器支持的特性)

J5最初的一部分Web Workes API引入了一个接口,能使代码运行不占用浏览器UI线程时间,现已成为独立的规范(https://www.w3.org/TR/workers)

6.5 加载外部文件

// 调用过程为阻塞式的,当所有文件加载并执行完成,脚本才会运行,不会影响UI响应
importScripts('file1.js', 'file2.js')

七. Ajax

7.1 请求数据的常用技术

  • XMLHttpRequest(XHR): 最常用,允许异步发送和接收数据
 let url = '/data'
 let param = ['id = 1234', 'limit=20']
 let req = new XMLHttpRequest()
 req.onreadystatechange = function () {
	if (req.readyState === 4) { // 4: 所有消息接收完毕, 3: 接收到部分信息,但不是所有
	let responseHeaders = req.getAllResponseHeaders() // 获取响应头
	let data = req.responseText // 获取数据
	}
}
req.onerror= function () {
	// 出错
}

req.open('GET', url + '?' + params.join('&'), true)
req.setRequestHeader('X-Requested-with', 'XMLHttpRequest') // 设置请求头
req.send(null) // 发送请求
  • Dynamic script tag insertion 动态脚本注入: 跨域请求数据,速度很快(不能设置请求头,且只能是GET请求,不能设置超时处理或重试,失败了也可能不知道)
 let scriptElement = document.createElement('script')
 scriptElement .src = 'http://.....lib.js'
 document.getElementsByTagName('head').appendChild(scriptElement)

// 无论哪种格式的数据都需要封装在一个回调函数中
function jsonCallback(jsonString) {
	let data = eval('(' + jsonString + ')')
}
  • iframes
  • Comet
  • Multipart XHR : 客户端一个请求从服务端传送多个资源

7.2 发送数据(不关心接收的问题):XHR和信标(beacons)

信标(beacons): 类似动态脚本注入

7.3 XML

7.4 JSON、JSONP

json:

[{'id': 1, 'name': 'test1'}, {'id': 3, 'name': 'test3'}, {'id': 3, 'name': 'test3'}]
// 优化(传输速度更快):
[[1, 'test1'], [2, 'test2'], [3, 'test3']]

jsonp: (相比于普通json数据,解析耗时更低):敏感数据不适用 —> 会被人使用动态脚本插入技术放到任何网站

parseJSON([{'id': 1, 'name': 'test1'}, {'id': 3, 'name': 'test3'}, {'id': 3, 'name': 'test3'}])

7.5 自定义格式(最优,速度最快的方式)

以特定字符串的方式传值,eg: name;age;email;address
可用split()分隔为数组

合并多个js文件

Apache Ant提供了合并多个文件的concat任务

压缩js文件: YUI Compressor,Gzip服务器压缩

缓存js文件

CDN内容分发网络

性能分析工具: YUI Profiler

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值