javascript小技巧

0.前言

本文参考网上的博客文章以及自己平时写项目时的一些心得体会总结的一些JavaScript代码的小技巧,会不定期更新,需要的朋友可以收藏一下。

1.Dom操作

在JavaScript的代码中,原则上是涉及到的Dom操作越少越好,因为Dom操作是非常耗费性能的。以下两份代码在Chrome浏览器(版本61.0.3163.79,64位) 中测试。

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>JavaScript Test</title>
</head>
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>

<script>
var count = 0;
console.time();
for (var i = 0; i < document.getElementsByTagName('li').length; i++) {
count++;
}
console.timeEnd();//0.35009765625ms
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>JavaScript Test</title>
</head>
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>

<script>
var count = 0;
console.time();
for (var i = 0; i < document.getElementsByTagName('div').length; i++) {
count++;
}
console.timeEnd();//0.0849609375ms
</script>
</body>
</html>

同理,如果用JavaScript动态生成Dom元素,可以用拼接字符串+innerHTML的方法,不用 createElement+createTextNode的方法,尤其是在动态生成的Dom元素比较多的情况下。下面是测试代码,测试环境同上。

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>JavaScript Test</title>
</head>
<body>
<ul>
</ul>

<script>
console.time();
var oUl = document.getElementsByTagName('ul')[0]
for (var i = 0; i < 100; i++) {
var oLi = document.createElement('li');
oLi.innerHTML = i;
oUl.appendChild(oLi);
}
console.timeEnd();//5.726806640625ms
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>JavaScript Test</title>
</head>
<body>
<ul>
</ul>

<script>
console.time();
var oUl = document.getElementsByTagName('ul')[0];
sLi = '';
for (var i = 0; i < 100; i++) {
sLi += '<li>'+i+'</li>'
}
oUl.innerHTML += sLi;
console.timeEnd();//1.376953125ms
</script>
</body>
</html>

2.Dom事件

一个简单的需求,比如想给ul下面的li加上点击事件,点击哪个li,就显示那个li的innerHTML。常规实现方法如下。

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>JavaScript Test</title>
</head>
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>

<script>
var oUl = document.getElementsByTagName('ul')[0];
var oLi = oUl.getElementsByTagName('li');
for (var i = 0, len = oLi.length; i < len; i++) {
oLi[i].addEventListener('click', function(){
alert(this.innerHTML);
});
}
</script>
</body>
</html>

但是上述代码有两个问题:

for循环,循环的是li,10个li就循环10次,绑定10次事件,100个就循环了100次,绑定100次事件。
如果li不是本来就在页面上的,是js渲染的未来元素,上面的写法是无效的。
事件委托的写法如下。

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>JavaScript Test</title>
</head>
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>

<script>
var oUl = document.getElementsByTagName('ul')[0];
oUl.addEventListener('click', function(ev){
var ev = ev || window.event;
var target = ev.target || ev.srcElement;
if (target.tagName.toLowerCase() == 'li') {
alert(target.innerHTML);
}
});
</script>
</body>
</html>

3.JavaScript对象深浅拷贝

对于JavaScript对象的深浅拷贝,个人认为有以下区别:

  1. 无论是浅拷贝还是深拷贝都只针对引用数据类型(如Object,Array)
  2. 浅拷贝只是将对象的引用地址进行拷贝,改变拷贝值,并没有开辟新的栈。
  3. 深拷贝会开辟新的栈,两个对象指向两个不同的地址。
  4. 注意下面三段代码的区别。三段代码分别是浅拷贝,单层深拷贝以及深拷贝。
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
//shallow clone
var me = { 'name': 'DeveloperYuan', 'sex': 'male', 'age': 24 };
var someone = me;
someone.age = 25;
console.log(someone);//{name: "DeveloperYuan", sex: "male", age: 25}
console.log(someone);//{name: "DeveloperYuan", sex: "male", age: 25}
//one-level deep clone
var me = { 'name': 'DeveloperYuan', 'sex': 'male', 'age': 24, 'skills':['php', 'javascript', 'python']};
var someone = Object.assign({}, me);
someone.name = 'passerby';
console.log(me);//{name: "DeveloperYuan", sex: "male", age: 24, 'skills':['php', 'javascript', 'python']}
console.log(someone);//{name: "passerby", sex: "male", age: 24, 'skills':['php', 'javascript', 'python']}
someone.skills[0] = 'c++';
console.log(me); //{name: "DeveloperYuan", sex: "male", age: 24, 'skills':['c++', 'javascript', 'python']}
console.log(someone); //{name: "passerby", sex: "male", age: 24, 'skills':['c++', 'javascript', 'python']}
//deep clone
function deepClone(obj) {
if (!obj && typeof(obj) !== 'object') {
return;
}
var cloneObj = obj.constructor === Array ? [] : {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof(obj[key]) === 'object') {
cloneObj[key] = obj[key].constructor === Array ? [] : {};
cloneObj[key] = deepClone(obj[key]);
} else {
cloneObj[key] = obj[key];
}
}
}
return cloneObj;
}

var me = {'name':'DeveloperYuan', 'sex':'male', 'age':24, 'skills':['php', 'javascript', 'python']};
var someone = deepClone(me);
someone.skills[0] = 'c++';
console.log(me); //{'name':'DeveloperYuan', 'sex':'male', 'age':24, 'skills':['php', 'javascript', 'python']};
console.log(someone); //{'name':'DeveloperYuan', 'sex':'male', 'age':24, 'skills':['c++', 'javascript', 'python']};

4.其他

  1. 扩展运算符( spread )
    扩展运算符( spread )是三个点(…)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。

    1
    2
    3
    console.log(...[1, 2, 3]);  // 1 2 3  
    console.log(1, ...[2, 3, 4], 5); // 1 2 3 4 5
    [...document.querySelectorAll('div')]; // [<div>, <div>, <div>]
  2. 数组去重(ES6)

    1
    [...new Set([1,2,1,'1','12','12'])];//[1, 2, "1", "12"]
  3. 字符串转浮点数和整数

    1
    2
    3
    4
    5
    6
    7
    8
    a = '12.21';
    b = +a;//12.21
    c = a | 0;//12
    d = ~~a;//12
    使用模板字符串
    name = 'DeveloperYuan';
    time = new Date();
    const message = `Hello ${name}, it's ${time} now`;
  4. 结构

    1
    2
    const data = {name:'dys', age:1} 
    const {name, age} = data

参考链接:

如何优雅的编写JavaScript代码
个人小结–javascript实用技巧和写法建议