重构
本文参考了曾探的《JavaScript设计模式与开发实战》。
提炼函数
如果一个函数过长,不得不加上若干注释才能让这个函数显得易读一些,那这些函数就很有必要进行重构。
如果在函数中有一段代码可以被独立出来,那我们最好把这些代码放进另外一个独立的函数中。这是一种很常见的优化工作,这样做的好处主要有以下几点。
- 避免出现超大函数。
- 独立出来的函数有助于代码复用。
- 独立出来的函数更容易被覆写。
- 独立出来的函数如果拥有一个良好的命名,它本身就起到了注释的作用。
合并重复的条件片段
比如把这段代码:
javascript
var paging = function( currPage ){
if ( currPage <= 0 ){
currPage = 0;
jump( currPage ); // 跳转
}else if ( currPage >= totalPage ){
currPage = totalPage;
jump( currPage ); // 跳转
}else{
jump( currPage ); // 跳转
}
};
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
修改成:
javascript
var paging = function( currPage ){
if ( currPage <= 0 ){
currPage = 0;
}else if ( currPage >= totalPage ){
currPage = totalPage;
}
jump( currPage ); // 把jump函数独立出来
};
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
把条件分支语句提炼成函数
比如把下面这段代码:
javascript
var getPrice = function( price ){
var date = new Date();
if ( date.getMonth() >= 6 && date.getMonth() <= 9 ){ // 夏天
return price * 0.8;
}
return price;
};
观察这句代码:
if ( date.getMonth() >= 6 && date.getMonth() <= 9 ){
// ...
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
修改成:
javascript
var isSummer = function(){
var date = new Date();
return date.getMonth() >= 6 && date.getMonth() <= 9;
};
var getPrice = function( price ){
if ( isSummer() ){ // 夏天
return price * 0.8;
}
return price;
};
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
合理使用循环
比如将下面这段代码:
javascript
var createXHR = function(){
var xhr;
try{
xhr = new ActiveXObject( 'MSXML2.XMLHttp.6.0' );
}catch(e){
try{
xhr = new ActiveXObject( 'MSXML2.XMLHttp.3.0' );
}catch(e){
xhr = new ActiveXObject( 'MSXML2.XMLHttp' );
}
}
return xhr;
};
var xhr = createXHR();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
修改成:
javascript
var createXHR = function(){
var versions= [
'MSXML2.XMLHttp.6.0ddd',
'MSXML2.XMLHttp.3.0',
'MSXML2.XMLHttp'
];
for ( var i = 0, version; version = versions[ i++ ]; ){
try{
return new ActiveXObject( version );
}catch(e){
}
}
};
var xhr = createXHR();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
提前让函数退出代替嵌套条件分支
比如将下面这段代码:
javascript
var del = function( obj ){
var ret;
if ( ! obj.isReadOnly ){ // 不为只读的才能被删除
if ( obj.isFolder ){ // 如果是文件夹
ret = deleteFolder( obj );
}else if ( obj.isFile ){ // 如果是文件
ret = deleteFile( obj );
}
}
return ret;
};
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
修改为:
javascript
var del = function( obj ){
if ( obj.isReadOnly ){ // 反转if表达式
return;
}
if ( obj.isFolder ){
return deleteFolder( obj );
}
if ( obj.isFile ){
return deleteFile( obj );
}
};
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
传递对象参数代替过长的参数列表
比如将下面的代码:
javascript
var setUserInfo = function( id, name, address, sex, mobile, qq ){
console.log( 'id= ' + id );
console.log( 'name= ' +name );
console.log( 'address= ' + address );
console.log( 'sex= ' + sex );
console.log( 'mobile= ' + mobile );
console.log( 'qq= ' + qq );
};
setUserInfo( 1314, 'sven', 'shenzhen', 'male', '137********', 377876679 );
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
修改为:
javascript
var setUserInfo = function( obj ){
console.log( 'id= ' + obj.id );
console.log( 'name= ' + obj.name );
console.log( 'address= ' + obj.address );
console.log( 'sex= ' + obj.sex );
console.log( 'mobile= ' + obj.mobile );
console.log( 'qq= ' + obj.qq );
};
setUserInfo({
id: 1314,
name: 'sven',
address: 'shenzhen',
sex: 'male',
mobile: '137********',
qq: 377876679
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
尽量减少参数数量
比如将下面这段代码:
javascript
var draw = function( width, height, square ){};
1
修改为:
javascript
// square 完全可以在函数内部自行计算出来,不需要从外部传入
var draw = function( width, height ){
var square = width * height;
};
1
2
3
4
2
3
4
少用三目运算符
如果条件分支逻辑简单且清晰,这无碍我们使用三目运算符:
javascript
var global = typeof window ! == "undefined" ? window : this;
1
但是像下面这样就不合适了:
javascript
if ( ! aup || ! bup ) {
return a === doc ? -1 :
b === doc ? 1 :
aup ? -1 :
bup ? 1 :
sortInput ?
( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
0;
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
合理使用链式调用
在JavaScript中,可以很容易地实现方法的链式调用,即让方法调用结束后返回对象自身,如下代码所示:
javascript
var User = function(){
this.id = null;
this.name = null;
};
User.prototype.setId = function( id ){
this.id = id;
return this;
};
User.prototype.setName = function( name ){
this.name = name;
return this;
};
console.log( new User().setId( 1314 ).setName( 'sven' ) );
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
或者这样写也可以:
javascript
var User = {
id: null,
name: null,
setId: function( id ){
this.id = id;
return this;
},
setName: function( name ){
this.name = name;
return this;
}
};
console.log( User.setId( 1314 ).setName( 'sven' ) );
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
使用链式调用的方式并不会造成太多阅读上的困难,也确实能省下一些字符和中间变量,但节省下来的字符数量同样是微不足道的。链式调用带来的坏处就是在调试的时候非常不方便,如果我们知道一条链中有错误出现,必须得先把这条链拆开才能加上一些调试log或者增加断点,这样才能定位错误出现的地方。
如果该链条的结构相对稳定,后期不易发生修改,那么使用链式调用无可厚非。但如果该链条很容易发生变化,导致调试和维护困难,那么还是建议使用普通调用的形式:
javascript
var user = new User();
user.setId( 1314 );
user.setName( 'sven' );
1
2
3
4
2
3
4
分解大型类
示例代码:
javascript
var Spirit = function( name ){
this.name = name;
this.attackObj = new Attack( this );
};
Spirit.prototype.attack = function( type ){ // 攻击
this.attackObj.start( type );
};
var spirit = new Spirit( 'RYU' );
spirit.attack( 'waveBoxing' ); // 输出:RYU:使用波动拳
spirit.attack( 'whirlKick' ); // 输出:RYU:使用旋风腿
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13