Js性能技巧

[高效的JavaScript]

本文会展示一些能提升 Web 应用性能的改变,其范围涉及 ECMAScript —— JavaScript 的核心语言、DOM 和文件加载。

[如何让你的JavaScript代码更加语义化] 0052-D

三、小结
代码的优化,需要考虑的维度很多。但是代码的优化并不是减少代码量,有的时候我们需要增加代码来提高代码的可阅读性。

正确标记变量
封装某个动作
注意函数的写法
不容易理解的东西,加注释

# [JavaScript:少一点条件语句] 0053-Dgone

[在js开发中,如何减少if else语句的使用]

第一步:优化if逻辑
第二步:尽量少使用else
可以使用 if + return
1 switch/case
2.hash 表
3.重构,用 OO 里面的继承或者组合

1
2
3
4
5
6
7
8
9
10
11
12
13
// # 2
var ceos = {"Apple":"Jobs", "microsoft":"Gates", "Google":"Larry"};
val = ceos[key];
// # 3
function getName(type) {
var data = {
monkey: 'monkey name',
cat: 'cat name',
dog: 'dog name'
}

return data[type] ? data[type] : data['dog'];
}

[如何无痛降低 if else 面条代码复杂度] 0053-D

JS逻辑判断的优雅写法

JS逻辑判断的优雅写法
这样写用到了es6里的Map对象,Map对象和Object对象有什么区别:

  1. Object对象有原型, 也就是说它有默认的key值在对象上面, 除非我们使用Object.create(null)创建一个没有原型的对象;
  2. Object对象中, 只能把String和Symbol作为key值, 但是在Map中,key值可以是任意值;
  3. 我们可以通过size属性很容易地得到一个Map的键值对个数,而对象的键值对个数只能手动确认。

用map实现二元判断逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 二元判断 map
const actions = new Map([
['guest_1', ()=>{/*do sth*/}],
['guest_2', ()=>{/*do sth*/}],
['guest_3', ()=>{/*do sth*/}],
['guest_4', ()=>{/*do sth*/}],
['guest_5', ()=>{/*do sth*/}],
['master_1', ()=>{/*do sth*/}],
['master_2', ()=>{/*do sth*/}],
['master_3', ()=>{/*do sth*/}],
['master_4', ()=>{/*do sth*/}],
['master_5', ()=>{/*do sth*/}],
['default', ()=>{/*do sth*/}],
])
/**
* 按钮点击事件
* @param {string} identity 身份标识:guest客态 master主态
* @param {number} status 活动状态:1 开团进行中 2 开团失败 3 开团成功 4 商品售罄 5 有库存未开团
*/
const onButtonClick = (identity,status)=>{
let action = actions.get(`${identity}_${status}`) || actions.get('default')
action.call()
}

判断条件写作正则作为map的key

1
2
3
4
5
6
7
8
9
10
11
12
13
const actions = ()=>{
const functionA = ()=>{/*do sth*/}
const functionB = ()=>{/*do sth*/}
return new Map([
[/^guest_[1-4]$/,functionA],
[/^guest_5$/,functionB],
//...
])
}
const onButtonClick = (identity,status)=>{
let action = [...actions()].filter(([key,value])=>(key.test(`${identity}_${status}`)))
action.forEach(([key,value])=>value.call())
}

这里Map的优势更加凸显,可以用正则类型作为key了,这样就有了无限可能,假如需求变成,凡是guest情况都要发送一个日志埋点,不同status情况也需要单独的逻辑处理,那我们可以这样写:

[巧用匿名函数重构你的代码]

按照重构的惯例,先指出代码中的坏味(Bad Smell):
定义冗长的重复配置
条件多变的集合过滤
说一不二的方法调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// # 1 
{
html: (function(fruits) {
return fruits.map(it => `<label><input type="checkbox" name="${it.name}" title="${it.title}" /> ${it.title}</label>`).join('');
}([
{name: 'apple', title: 'Apple'},
{name: 'banana', title: 'Banana'},
{name: 'orange', title: 'Orange'}
]))
}

// # 2
// bad
filterByName(filterBySex(filterByAge(students, 21), 'male'), 'LiLei');
// good
function filter(list, fn) {
let filtered = [];
for (let i = 0; i < list.length; i++) {
if (fn(list[i], i) === true) {
filtered.push(list[i]);
}
}
return filtered;
}
filter(students, function(member) {
return member.name === 'LiLei' && member.age === 21 && member.sex === 'male';
});

// # 3
function execute(data, fn) {
// 最小执行单元
let single = it => {
try {
return {
status: 'success',
data: fn(it)
};
} catch (e) {
return {
status: 'failed',
error: e.toString()
}
}
};

if (Array.isArray(data)) {
return {
status: 'success',
data: data.map(single)
}
} else {
return single(data);
}
}

function multiple(inNum) {
return execute(inNum, it => {
if (isNaN(parseFloat(it))) {
throw new Error('The input is not a number');
}

return it * 2;
});
}

[12个有关于JavaScript的小技巧]

1、使用 !!操作符转换布尔值:要变量的值为:0、null、” “、undefined 或者NaN 都将返回的是 false,反之返回的是 true。比如下面的示例:
2、使用 + 将字符串转换成数字 +”1234” // 1234 +’sdfs’//NaN
6、检测对象中属性 ‘querySelector’ in document
7、获取数组中最后一个元素: array.slice(-1),array.slice(-3)// 4,5,6
8、数组截断 array.length = 3
9、替换所有string.replace(/hn/g, “ana”)
10、合并数组 array1.push.apply(array1, array2)
11、将 NodeList 转换成数组 [].slice.call(elements) |var arrayElements = Array.from(elements);
12、数组元素的洗牌 list.sort(function() { Math.random() - 0.5 })

3、并符条件 4、使用 || 运算符
5、在循环中缓存 array.length

# [19+ 个 JavaScript 快速编程技巧 — SitePoint] 0059-Dgone
19 双位取反运算符速记法 ~~4.9 === 4 //true

[JavaScript 性能优化的小知识总结]

避免全局查找

在一个函数中会用到全局对象存储为局部变量来减少全局查找,因为访问局部变量的速度要比访问全局变量的速度更快些

定时器

如果针对的是不断运行的代码,不应该使用 setTimeout,而应该是用 setInterval,因为 setTimeout 每一次都会初始化一个定时器,而 setInterval 只会在开始的时候初始化一个定时器

字符串连接

如果要连接多个字符串,应该少使用 +=,如

s+=a;
s+=b;
s+=c;

应该写成 s+=a + b + c;

而如果是收集字符串,比如多次对同一个字符串进行 += 操作的话,最好使用一个缓存,使用 JavaScript 数组来收集,最后使用 join 方法连接起来

##避免 with 语句
和函数类似 ,with 语句会创建自己的作用域,因此会增加其中执行的代码的作用域链的长度,由于额外的作用域链的查找,在 with 语句中执行的代码肯定会比外面执行的代码要慢,在能不使用 with 语句的时候尽量不要使用 with 语句。

## 数字转换成字符串
般最好用 "" + 1来将数字转换成字符串,虽然看起来比较丑一点,但事实上这个效率是最高的,性能上来说:
(“” +) > String() > .toString() > new String()

浮点数转换成整型

很多人喜欢使用 parseInt(),其实 parseInt() 是用于将字符串转换成数字,而不是浮点数和整型之间的转换,我们应该使用 Math.floor() 或者 Math.round()

各种类型转换

var myVar ="3.14159",
str =""+ myVar,//  to string  
i_int =~~myVar,  //  to integer  
f_float =1* myVar,  //  to float  
b_bool =!!myVar,  /*  to boolean - any string with length
                    and any number except 0 are true */
array =[myVar];  //  to array  

如果定义了 toString() 方法来进行类型转换的话,推荐显式调用 toString(),因为内部的操作在尝试所有可能性之后,会尝试对象的 toString() 方法尝试能否转化为 String,所以直接调用这个方法效率会更高

多个类型声明

在 JavaScript 中所有变量都可以使用单个 var 语句来声明,这样就是组合在一起的语句,以减少整个脚本的执行时间,就如上面代码一样,上面代码格式也挺规范,让人一看就明了。

插入迭代器

如 var name=values[i]; i++; 前面两条语句可以写成 var name=values[i++]

使用直接量

var aTest =newArray();//替换为
var aTest =[];
var aTest =new Object();//替换为
var aTest ={};
var reg =new RegExp();//替换为
var reg =/../;
//如果要创建具有一些特性的一般对象,也可以使用字面量,如下:
var oFruit =new Object();
oFruit.color ="red";
oFruit.name ="apple";
//前面的代码可用对象字面量来改写成这样:
var oFruit ={ color:"red", name:"apple"};

使用 DocumentFragment 优化多次 append

一旦需要更新 DOM, 请考虑使用文档碎片来构建 DOM 结构,然后再将其添加到现存的文档中。

var frag = document.createDocumentFragment();
for(var i =0; i <1000; i++){
    var el = document.createElement('p');
    el.innerHTML = i;
    frag.appendChild(el);
}
document.body.appendChild(frag);

使用一次 innerHTML 赋值代替构建 dom 元素

对于大的 DOM 更改,使用 innerHTML 要比使用标准的 DOM 方法创建同样的 DOM 结构快得多。

var frag = document.createDocumentFragment();
for(var i =0; i <1000; i++){
    var el = document.createElement('p');
    el.innerHTML = i;
    frag.appendChild(el);
}
document.body.appendChild(frag);
//可以替换为:
var html =[];
for(var i =0; i <1000; i++){
    html.push('<p>'+ i +'</p>');
}
document.body.innerHTML = html.join('');

通过模板元素 clone,替代 createElement
使用 firstChild 和 nextSibling 代替 childNodes 遍历 dom 元素
删除 DOM 节点
使用事件代理
重复使用的调用结果,事先保存到局部变量
注意 NodeList
优化循环
展开循环
避免双重解释
缩短否定检测
条件分支
使用常量
避免与 null 进行比较
避免全局量
尊重对象的所有权
循环引用
通过 javascript 创建的 dom 对象,必须 append 到页面中
释放 dom 元素占用的内存
释放 javascript 对象
避免 string 的隐式装箱
松散耦合
性能方面的注意事项
避免错误应注意的地方
== 和 === 的区别
不要使用生偏语法
函数返回统一类型
总是检查数据类型
何时用单引号,何时用双引号
部署

knowledge is no pay,reward is kindness
0%