maintainable javascript
TRANSCRIPT
Maintainable Javascript编写高效可维护的JS代码
@HectorGuo
可维护性
Programs are meant to be read by humans and only incidentally for computers to execute
程序主要是给人读的,其次才是让计算机拿来
运行的
- H. Abelson and G. Sussman,
The Structure and Interpretation of Computer Programs
代码规范的作用
帮助团队相互间更好地通过代码来交流
4空格缩进
Tab缩进
if (JSV.typeOf(items) === "array") {
for (x = 0, xl = properties.length; x < xl; ++x) {
itemSchema = items[x] || additionalProperties;
if (itemSchema !== false) {
itemSchema.validate(properties[x], report, instance, schema, x);
} else {
report.addError(instance, schema, "additionalProperties",
"Additional items are not allowed", itemSchema);
}
}
}
if (JSV.typeOf(items) === "array") {
for (x = 0, xl = properties.length; x < xl; ++x) {
itemSchema = items[x] || additionalProperties;
if (itemSchema !== false) {
itemSchema.validate(properties[x], report, instance, schema,
x);
} else {
report.addError(instance, schema, "additionalProperties",
"Additional items are not allowed", itemSchema);
}
}
}
https://github.com/jdc0589/JsFormat
/**
* A low-level selection function that works with Sizzle's compiled
* selector functions
* @param {String|Function} selector A selector or a pre-compiled
* selector function built with Sizzle.compile
* @param {Element} context
* @param {Array} [results]
* @param {Array} [seed] A set of elements to match against
*/
select = Sizzle.select = function( selector, context, results, seed ) {
var i, tokens, token, type, find,
compiled = typeof selector === "function" && selector,
match = !seed && tokenize( (selector = compiled.selector ||
selector) );
...
每个方法都应该有详细的注释
switch(mode) {
case 1: // to B
changeTo('B');
break;
case 2: // mix A and B
mixWith('A', 'B');
break;
case 3: // to A
changeTo('A');
break;
case 4: // mix B and C
mixWith('B', 'C')
break;
}
较难理解的代码
命名
● 用更易理解的名字给变量(Variables)/函数(Functions)命名
○ 不要在意长度
https://www.allacronyms.com/
命名
● 用更易理解的名字给变量(Variables)/函数(Functions)命名
○ 不要在意长度
● 变量应该是名词
● 函数应该以动词开头(如 getName() )○ 返回布尔类型的函数应该以“is”或“has”开头(如 isVaild(), hasItem() )
● 避免使用无意义的名字,如 foo,bar,temp
if (wl && wl.length) {
for (var i = 0; i < wl.length; i++) {
p = wl[i];
if (s.hasOwnProperty(p)) {
r[p] = s[p];
}
}
}
那些年,我们踩过的坑
Do we really know JS ?
var result = 1;
result == 1 // true
result == '1' // true
不要过于相信 == 的兼容性
[] == []
var a = [0];
if ([0]) {
console.log(a == true);
} else {
console.log("go here");
}
0.2 - 0.1 === 0.1
0.8 - 0.6 === 0.2
注意浮点数的运算
// true
// false
<button id="btn1">Click btn1</button>
<button id="btn2">Click btn2</button>
<button id="btn3">Click btn3</button>
for(var i = 1; i <= 3; i++){
$.on('#btn'+i, 'click', function(){
console.log('You are clicking:', 'btn' + i);
});
}
执行循环时,时刻注意作用域
// You are clicking: btn4
<button id="btn1">Click btn1</button>
<button id="btn2">Click btn2</button>
<button id="btn3">Click btn3</button>
for(var i = 1; i <= 3; i++){
$.on('#btn'+i, 'click', function(){
console.log('You are clicking:', 'btn' + i);
});
}
执行循环时,时刻注意作用域
// You are clicking: btn4
http://latentflip.com/loupe
<button id="btn1">Click btn1</button>
<button id="btn2">Click btn2</button>
<button id="btn3">Click btn3</button>
var handleClick = function(id) {
$.on('#'+id, 'click', function(){
console.log('You are clicking:', id);
});
}
for(var i = 1; i <= 3; i++){
handleClick('btn'+i);
}
执行循环时,时刻注意作用域
http://javascript-puzzlers.herokuapp.com/
高效性
编写高效的Javascript
● JSON比XML更快
● 高效地使用数组
● 尽量减少内存的占用
● 使用模版
JSON比XML更快
● 使用原生的JSON方法
var data = {a:'this is json data'};
var jsonStr = JSON.stringify(data);
var jsonData = JSON.parse(jsonStr);
数组内部运作
var a = new Array();
a[0] = 1;
a[1] = 2.3;
a[2] = 'str';
Type: Int Array
数组内部运作
var a = new Array();
a[0] = 1;
a[1] = 2.3;
a[2] = 'str';
Type: Int Array
Type: Int Array 1
数组内部运作
var a = new Array();
a[0] = 1;
a[1] = 2.3;
a[2] = 'str';
Type: Int Array
Type: Int Array 1
Type: Float Array 1 2.3
数组内部运作
var a = new Array();
a[0] = 1;
a[1] = 2.3;
a[2] = 'str';
Type: Int Array
Type: Int Array 1
Type: Float Array 1 2.3
Type: Var Array 1 2.3 ‘str’
给数组预分配长度
var a = new Array();
for (var i = 0; i < 100; i++){
a.push(i + 2);
}
SLOW
var a = new Array(100);
for (var i = 0; i < 100; i++){
a[i] = i + 2;
}
FAST
混合类型的数组,预定义类型
var a = new Array(100);
for (var i = 0; i < a.length; i++){
a[i] = i;
}
...
// 数组操作
...
a[99] = 'str';
SLOW
var a = new Array(100);
a[0] = 'hint';
for (var i = 0; i < a.length; i++){
a[i] = i;
}
...
// 数组操作
...
a[99] = 'str';
FAST
Delete会迫使引擎进行类型转变(变慢)
var a = new Array(100);
...
for (var i = 0; i < 100; i++){
a[i] = [1,2,3];
}
...
delete a[23];
SLOW
var a = new Array(100);
...
for (var i = 0; i < 100; i++){
a[i] = [1,2,3];
}
...
a[23] = 0;
FAST
缓存数组长度,避免重复性地访问属性
var a = new Array(100);
var total = 0;
for (var item in a){
total += item;
}
a.forEach(function(item){
total += item;
});
for (var i = 0; i < a.length; i++){
total += a[i];
}
SLOW
var a = new Array(100);
var total = 0;
var cachedLength = a.length;
for (var i = 0; i < cachedLength; i++){
total += a[i];
}
FAST
https://jsperf.com/for-vs-foreach/37
缓存数组长度,避免重复性地访问属性
var a = new Array(100);
var total = 0;
for (var item in a){
total += item;
}
a.forEach(function(item){
total += item;
});
for (var i = 0; i < a.length; i++){
total += a[i];
}
SLOW
var a = new Array(100);
var total = 0;
var cachedLength = a.length;
for (var i = 0; i < cachedLength; i++){
total += a[i];
}
FAST
高效地使用数组(Best Practices)
● 给数组预分配长度
● 混合类型的数组,预定义类型
● Delete会迫使引擎进行类型转变(变慢)
● 缓存数组长度,避免重复性地访问属性
哪些条件下,会影响内存的分配?
● 直接或间接地使用 new ○ 给对象预留内存
● Runtime下强制执行GC(垃圾回收)
● 每次定义/引用对象的时候
什么是垃圾回收(Garbage Collection)?
● 当内存池已满,已经无法再给新对象分配内存时,会把之前使用过的且再也
没有被引用的对象清空,腾出内存供新对象使用。
V8引擎垃圾回收(GC)机制
V8引擎垃圾回收(GC)机制
● Young Generation有更高的被回收率
V8引擎垃圾回收(GC)机制
To Space
From Space GC时使用
V8引擎垃圾回收(GC)机制
To Space
From Space GC时使用
V8引擎垃圾回收(GC)机制
未被分配的内存
From Space GC时使用
A
分配给对象A
V8引擎垃圾回收(GC)机制
未被分配的内存
From Space GC时使用
A
分配给对象B
B
V8引擎垃圾回收(GC)机制
未被分配的内存
From Space GC时使用
A
分配给对象C
B C
V8引擎垃圾回收(GC)机制
未被分配的内存
From Space GC时使用
A B C D
不够用了!
V8引擎垃圾回收(GC)机制
未被分配的内存
From Space GC时使用
A B C
GC开始!
程序暂停!
V8引擎垃圾回收(GC)机制
To Space
未被分配的内存A B C
空间被交换!
V8引擎垃圾回收(GC)机制
To Space
未被分配的内存A B C
正在使用/被引用的对象
V8引擎垃圾回收(GC)机制
To Space
未被分配的内存A B C
正在使用/被引用的对象
V8引擎垃圾回收(GC)机制
To Space
未被分配的内存A B C
正在使用/被引用的对象
复制
V8引擎垃圾回收(GC)机制
未被分配的内存A B C
正在使用/被引用的对象
未被分配的内存A C
V8引擎垃圾回收(GC)机制
From Space
未被分配的内存A C
V8引擎垃圾回收(GC)机制
From Space
未被分配的内存A C D
分配给对象D
每次垃圾回收都会暂停程序执行(卡顿/掉帧)
内存泄漏
内存泄漏
16ms!JS / CSS > 计算样式 > 布局 > 绘制 > 渲染层合并
内存泄漏
Slow Object vs Fast Object
function SlowPurchase(price) {
this.price = price;
this.total = 0;
this.x = 1;
}
var slow = new SlowPurchase(25);
// x没用,我删掉它
delete slow.x;
SLOW
function FastPurchase(price) {
this.price = price;
this.total = 0;
this.x = 1;
}
var fast = new FastPurchase(25);
FAST
Slow Object会占用更多的内存
DOM泄漏 var treeRef = $('#tree');
var leafRef = $('#leaf');
$('body').remove(treeRef);
// 此时 #tree 并没有被回收,
// 因为还有 treeRef 对象在引用
// 好,那就清空它
treeRef = null;
// 此时 #tree 还是没有被回收,
// 因为还有 leafRef 对象在间接引用
//(leafRef.parentNode)
leafRef = null;
// 好了,这次 #tree 可以被回收了
“Premature optimization is the root of all evil”
- Donald Knuth
该优化的时候再优化
内存监测工具
● performance.memory● 任务管理器
● Chrome Dev Tools
https://speakerdeck.com/addyosmani/javascript-memory-management-masterclass
Memory-diagnosis
https://www.smashingmagazine.com/2012/11/writing-fast-memory-efficient-javascript/
模版的发展var data = {
"result": [
{
"title": "Strawberry"
},{
"title": "Vanilla"
},{
"title": "Ice"
}
]
};
var template = '<ul>';
for (var i = 0; i < data.result; i++) {
template += '<li id="item' + i +
'"><strong>Title:</strong>' +
data.result[i].title +
'</li>';
}
template += '</ul>';
$('#container').html(template);
字符串拼接
var data = {
"result": [
{
"title": "Strawberry"
},{
"title": "Vanilla"
},{
"title": "Ice"
}
]
};
<!-- html -->
<script type="text/mustache-template" id="mustache">
<ul>
{{#result}}
<li><strong>Title:</strong> {{title}}</li>
{{/result}}
</ul>
</script>
// Javascript
var template = $('#mustache').html()
var html = Mustache.to_html(template, data);
$('#container').html(html);
模版引擎
数据绑定
数据绑定(Vue.js)
// javascript
var data = {
"result": [
{
"title": "Strawberry"
},{
"title": "Vanilla"
},{
"title": "Ice"
}
]
};
var app = new Vue({
el: '#container',
data: data.result
});
<!-- html -->
<div id="container">
<ul>
<li v-for="item in data">
<strong>Title:</strong>
{{item.title}}
</li>
</ul>
</div>
资源链接
JS性能比较:
https://jsperf.com/
V8引擎工作原理呈现工具:
http://mrale.ph/irhydra/2/
Event Loop原理呈现工具:
http://latentflip.com/loupe
Practical Performance Tips to Make Your HTML JavaScript Faster(微软教程):
https://www.youtube.com/watch?v=dk75fqFXwCE&list=PLMywOXqBTbMVgzv_oGGmdRTAPl2URqopE&index=8
Maintainable Javascript:https://www.youtube.com/watch?v=3MejbqcMC08&list=PLMywOXqBTbMVgzv_oGGmdRTAPl2URqopE&index=7
Thanks!