如何提高javascript性能
一、总原则
减少操作:js引擎,业务代码
本文主要从变量访问和算法逻辑讨论
二、变量访问
数据类型
数据存放的位置不同决定了数据访问的速度不同。JavaScript中有四种基本的数据类型:
-
字面量 (Literal values)
变量存储的是实际数值。JavaScript字面量类型有:字符串,数字,布尔值,对象,数组,函数, 正则表达式,NULL,undefined -
变量 (Vriables)
用vars声明的变量用于存储数据值 -
数据项 (Array items)
-
对象成员 (Object members)
NOTES:
a. 前两类存储在栈中,后两项存储在堆中
b. 前两类访问速度高于后两种
作用域
声明一个函数实际是创建一个Function类的实例
在函数创建时,建立一个[[Scope]]的内部属性,定义了该函数可访问的数据,称为作用域链。创建器作用域链 = 全局变量:browser,window, document等
在函数运行时,建立一个‘运行期上下文’的内部属性,定义了该函数运行期间作用域链等执行环境。运行期作用域链 = [[Scope]]作用域链 + 局部变量,参数,this,参数等,按照栈的方式存储
实例1: 定义以下函数
function add(num1, num2) {
const sum = num1 + num2;
return sum
}
创建期间的作用域链
image.png
执行函数时作用域链
image.png
实例2: 定义以下闭包
function assignEvents() {
var id = "123"
document.getElementById('save-btn').onclick = function(event) {
saveDocument(id)
}
}
创建期闭包函数作用域链
image.png
执行期闭包函数作用域链
image.png
结论:变量在作用域链的位置越深越慢,全局最慢,局部最快
提升:用本地局部变量缓存全局变量等访问速度慢的变量
三、优化实例
3.1 局部变量缓存
多次访问document的函数
function initUI(){
var bd = document.body,
links = document.getElementsByTagName_r("a"),
i = 0,
len = links.length;
while(i < len){
update(links[i++]);
}
document.getElementById("go-btn").onclick = function(){
start();
};
bd.className = "active"
}
缓存document后
function initUI(){
var doc = document,
bd = doc.body,
links = doc.getElementsByTagName_r("a"),
i = 0,
len = links.length;
while(i < len){
update(links[i++]);
}
doc.getElementById("go-btn").onclick = function(){
start();
};
bd.className = "active";
}
3.2 字符串连接
使运算符时,尽量使用+=,-=、=、=等运算符号,而不是直接进行赋值运算。
实例1
如果是追加字符串,最好使用s+=anotherStr
;而不是要使用s=s+anotherStr
实例2
拼接多个字符串
// bad
x+=a; x+=b; x+=c;
/// good
x+= a + b + c;
3.3 原型链
对象的继承是通过原型对象实现的
实例1
定义如下对象
var book = {
title: "High Performance JavaScript",
publisher: 'Yahoo'
}
原型链为
image.png
实例2
定义如下对象
function Book(title, publisher){
this.title = title;
this.publisher = publisher;
}
Book.prototype.sayTitle = function(){
alert(this.title);
};
var book1 = new Book("High Performance JavaScript", "Yahoo! Press");
alert(book1 instanceof Book); //true
alert(book1 instanceof Object); //true
book1.sayTitle(); //"High Performance JavaScript"
book1.toString(); //"[object Object]"
image.png
结论:对象成员在原型链上的位置越深速度越慢
提升:减少.的操作,缓存对象成员的值
四、算法与流程控制优化
4.1 循环语句
主要进行下面几种优化方案:
1 减少对象成员和数组项查找次数
2 减少迭代次数
3 缓存中间值
for (var i=0; i < items.length; i++){
process(items[i]);
}
上面的代码每次循环要做的操作:
1. 读取属性:items.length
2. 执行比较:i < items.length
3. 判断条件:i < items.length === true
4. 自加操作:i++
5. 数据查找:items[i]
6. 函数调用:process(items[i])
优化后
for (var i=0, l=items.length; i < l ; i++){
process(items[i])
}
4.2 条件判断
主要是if-else和switch
1. 将常用的条件放在前面
if (value < 5) {
//do something
// <5为高概率事件
} else if (value > 5 && value < 10) {
//do something
// 5-10为中概率事件
} else {
//do something
}
2 查表法
适合大量离散数据的条件判断
| |
优化后
//define the array of results
var results = [result0, result1, result2, result3, result4]
//return the correct result
return results[value];
或使用策略模式
//define the array of resultsvar results = [result0, result1, result2, result3, result4]//return the correct result
return results[value];