javascript性能优化
-
优化页面加载时间
-
html标签加载顺序
浏览器加载和执行script标签引入的js时,会暂停页面渲染,直到代码加载并执行。通常把script标签放到html文档body内容的后面,即是</body>前面
-
javascript文件的gzip编码传输
gzip编码可以确保javascript文件、css、html和文本文件以最高效率传输给浏览器
-
缩编、编译、混淆
jsMin 缩编 uglify 混淆
-
请求时才延时加载javascript文件
浏览器遇到script文件时,会阻塞页面加载其他文件,直到js完成解析和执行,这样可以防止出现竞争态势,保证js代码按顺序执行,以免后面的代码先执行。但是需要加载多个js文件时,会降低效率。
function loadScript (src, onLoad) { var scriptTag = document.createElement('script') scriptTag.src = src if (typeof onLoad === 'function') { scriptTag.onload = onLoad scriptTag.onreadstatechange = function () { if (scriptTag.readyState === 4) { onLoad() } } } document.body.appendChild(scriptTag) } loadScript('./src.js', function () { console.log(111) })
-
-
优化文档对象的操作
-
实现对页面元素的最小访问
尽量把获得的页面元素的引用用变量保存下来
-
尽量利用已有元素
使用已保存的页面元素引用
-
离线DOM的利用
-
使用css而非javascript来操控页面样式
通过javascript可以设置页面元素的style属性来操控元素的页面样式。因为设置页面元素的style属性的时候,会引起浏览器的重排操作,所以尽量使用css而非javascript来操控页面样式。在某些情况下非要使用,有两种方法可以优化操作
/** 1. 把要设置的页面样式一次性设置给页面元素,可以把页面样式放到css的类中,并给页面元素设置相应的类名 */ // js部分 var ele = document.getElementById('#box') ele.className += ' box' // css部分 .box { width: 100px; height: 100px; background-color: red; } /** 2. 先把要设置的页面元素的display属性设为none(此时设置style不会引起浏览器的重排),再设置它的style属性,别忘了设置完成之后display属性设置回去 */ // js部分 var ele = document.getElementById('#box') ele.style.display = 'none' ele.style.width = '100px' ele.style.height = '100px' ele.style.display = 'block'
-
-
提升DOM事件性能
-
委托事件至父元素
当子元素很多时,为每个子元素监听事件的开销很大,利用冒泡的原理把子元素的事件委托给父元素处理
<body> <ul> <li>列表1</li> <li>列表2</li> <li>列表3</li> <li>列表4</li> <li>列表5</li> <li>列表6</li> </ul> <button>添加列表</button> <script> ~function(){ let ul = document.getElementsByTagName('ul')[0], button = document.getElementsByTagName('button')[0] // 委托给父元素 ul.addEventListener('click',function (e) { if (e.target.nodeName === 'LI') { console.log(e.target.innerHTML) } }, false) button.addEventListener('click', e => { let li = document.createElement('li') li.innerHTML = '创建的一个心列表' ul.appendChild(li) }, false) }() </script> </body>
-
使用框架化处理频密发出的事件
~function () { let scrollTopPosition = 0, scrollLeftPosition = 0, body = document.body, header = document.getElementById('header') function onScroll () { scrollTopPosition = body.scrollTop scrollLeftPosition = body.scrollLeft console.log(111) } function writeScrollPosition () { header.innerHTML = `scrollLeftPosition: ${scrollTopPosition}, scrollTopPosition: ${scrollLeftPosition}` } document.addEventListener('scroll', onScroll, false) window.setInterval(writeScrollPosition, 500) }() 去抖 和 节流 // 去抖 let button = document.getElementsByTagName('button')[0] function responseClick (e) { console.log(111) } function debounce (fn) { let timer = null return function (e) { let _this = this window.clearTimeout(timer) timer = window.setTimeout(function() { fn.call(_this, e) timer = null }, 500); } } button.addEventListener('click', debounce(responseClick), false) // 节流 ~function(){ let button = document.getElementsByTagName('button')[0] function throttle (fn) { let timer = null, start = new Date().getTime(), end = 0 return function (e) { let _this = this window.clearTimeout(timer) end = new Date().getTime() if (end - start > 500) { fn.call(this, e) start = end return } timer = window.setTimeout(function () { fn.call(_this, e) timer = null }, 500) } } button.addEventListener('click', throttle(function(e){ console.log(this) }), false) }()
-
-
提升函数性能
提升javascript程序的性能很大程度上是提升javascript代码的执行效率,函数是效率改进的主要部分,函数中的每一行代码有可能影响到整体代码的运行速度和性能
- 使用记忆功能保存先前函数的返回结果
一个函数被多次调用,并且很可能会多次传入相同的参数,所以我们需要确保无论在什么情况下当同一函数以相同参数执行多次时,都可以将第一次执行结果保存在一个变量中,以代替同一函数代码的多次执行
- 使用记忆功能保存先前函数的返回结果
// 应用一般性记忆功能至函数
/**
传入一个函数,返回一个带有记忆功能的函数
*/
function memoize (fn) {
return function () {
let propertyName
fn.storage = fn.storage || {}
propertyName = Array.prototype.join.call(arguments, '|')
if (fn.storage[propertyName]) {
return fn.storage(propertyName)
}
fn.storage[propertyName] = fn.apply(this, arguments)
return fn.storage[propertyName]
}
}
```
-
使用正则表达式实现更快速的字符串操作
-
更快速的使用数组
-
快速创建数组
var arr = new Array() var arr = [] 创建并初始化数组最快的方法是后者 前者需要一个额外的步骤来获得Array类型的构造函数,并执行new关键词来创建它
-
快速进行数组循环
var arr = [] for (let i = 0, len = arr.length; i < len; i++) { ... }
-
避免在循环中创建函数
-
-
转移密集型任务至Web Worker
/** 主页面代码 */ let workerThread = new Worker('worker.js') // worker线程传递回来数据时触发 workerThread.addEventListener('message',function (e) { }, false) // 向线程发送数据并唤醒线程 workerThread.postMessage('数据') /** worker.js */ // 主页面发送数据时触发事件 self.addEventListener('message', e => { // todo self.postMessage('传递数据到主页面') }, false)