以下都是一些建议,没有哪些是必须严格遵守的标准。
具体是否需要重构,以及如何进行重构,这需要我们根据系统的类型、项目工期、人力等外界因素一起决定。
22.1 提炼函数
避免出现过长的函数,函数体内的逻辑应该清晰明了。
比如:
var getUserInfo = function(){
ajax('http://xxxx', function( data ){
console.log( 'userId:' + data.userId);
console.log( 'userName:' + data.userName:);
console.log( 'nickName' + data.nickName)
})
}
可以重构为:
var printDetails = function( data ){
console.log( 'userId:' + data.userId);
console.log( 'userName:' + data.userName:);
console.log( 'nickName' + data.nickName);
}
var getUserInfo = function(){
ajax('http://xxxx', function( data ){
printDetails( data )
})
}
22.2 合并重复的条件片段
如果一个函数体内有一些条件分支语句,而这些条件分支语句内部散步了一些重复的代码,那么就有必要进行合并去重工作。
var padding = function( currPage ) {
if( currPage <= 0 ){
currPage = 0;
jump( currPage ); //跳转
} else if ( currPage >= totalPage ){
currPage = totalPage;
jump( currPage ); //跳转
} else {
jump( currPage ); //跳转
}
}
以上代码进行重构后:
var padding = function( currPage ) {
if( currPage <= 0 ){
currPage = 0;
} else if ( currPage >= totalPage ){
currPage = totalPage;
}
jump( currPage ); //跳转,将jump函数独立出来
}
22.3 把条件分支语句提炼成函数
在程序设计中,复杂的条件分支语句是导致程序难以阅读和理解的重要原因,而且容易导致一个庞大的函数。
将条件分支语句提炼为一个单独函数,是一个很好的方法。
以下代码:
var getPrice = function( price ){
var data = new Date();
if ( data.getMonth() >= 6 && data.getMonth() <= 9 ) { //夏天
return price * 0.8;
}
return price
}
可重构为:
var isSummer = function() {
var data = new Date();
return data.getMonth() >= 6 && data.getMonth() <= 9;
}
var getPrice = function( price ){
if ( isSummer() ) { //夏天
return price * 0.8;
}
return price
}
22.4 合理使用循环
在函数体内,如果有些代码实际上负责的是一些重复性的工作,那么合理利用循环不仅可以完成同样的功能,还可以使代码量更少。
下面这个重构代码真好,推荐大家学习:
原代码:
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()
重构之后:
var createXHR = function(){
var versions = ['MSXML2.XMLHttp.6.0','MSXML2.XMLHttp.3.0','MSXML2.XMLHttp'];
for(var i = 0, len = versions.length; i < len;i++ ){
try{
return new ActiveXObject(version)
} catch (e){
}
}
}
var xhr = createXHR()
精彩之处在于很好的运用for循环处理了代码重复的问题。
22.5 提前让函数退出代替嵌套条件分支
摒弃函数只有一个出口的想法,尽量减少if else语句嵌套,如果满足条件不想再继续执行,请直接 return 退出循环。
var del = function( obj ){
var ret;
if( !obj.isReadOnly ){ //不为只读的才能被删除
if ( obj.isFolder ) { //如果是文件件
ret = deleteFolder( obj )
} else if ( obj.isFile ) { //如果是文件
ret = deleteFile( obj )
}
}
return ret
}
上面这段代码理解起来比较有难度,为了看到函数出口,必须得读完函数所有内容,有时候,很多内容是你不用也不想看到的。
重构之后:
var del = function( obj ){
if ( obj.isReadOnly ){
return
}
if ( obj.isFolder ){
return deleteFolder( obj );
}
if ( obj.isFile ){
return deleteFile( obj );
}
}
看完感觉就两个字:清爽!
22.6 传递对象参数代替过长的参数列表
避免为函数传递过多的参数,参数越多,函数就越难理解和使用。搞反参数位置也会出错。
最好的传递方式,就是把参数放进一个对象内,然后把该对象传递给函数。
var obj = {
id: 123,
name: 'wang',
age: 20,
sex: '男'
}
var showInfo = function( obj ){
console.log(obj.id);
console.log(obj.name);
console.log(obj.age);
console.log(obj.sex);
}
22.7 尽量减少参数数量
如果一个函数不需要传入任何参数就可以使用,这种函数是深受人们喜爱的。
比如,现在有一个画图函数 draw ,它现在只能绘制正方形,接收了三个参数。分别是width,height,square
var draw = function( width, height, square ){}
但实际中,square是可以通过width和height算出来的。所以上面这段代码可以变成:
var draw = function( width, height ){
var square = width * height;
}
所以,请尽量较少函数的参数。
22.8 少用三目运算符
如果条件分支逻辑简单清晰,可以使用三目运算符,但是如果分支逻辑非常复杂,最好不要使用三目运算符。
三目运算符和if else 性能一样,可是在处理复杂逻辑时候,三目运算符会损失代码可读性和可维护性。
22.9 合理使用链式调用
var user = {
id: null,
name: null,
setId: function( id ) {
this.id = id;
return this;
},
setName: function( name ){
this.name = name;
return this;
}
}
user.setId( 1314 ).setName( 'sven' );
22.10 分解大型类
var Spirit = function( name ){
this.name = name;
}
Spirit.prototype.attack = function( type ) {
if( type == 'waveBoxing' ){
console.log( this.name + ':使用波动拳');
} else if ( type == 'whirlKick' ){
console.log( this.name + ':使用旋风腿');
}
}
var spirit = new Spirit('RYU');
spirit.attack('waveBoxing');
spirit.attack('whirlKick');
在以上代码中,我们可以看到 Spirit.prototype.attack 这个方法太过于庞大,实际上,它完全有必要作为一个单独的类存在。
面向对象设计鼓励将行为分布在合理数量的更小对象之中。
重构之后:
var Attack = function( spirit ){
this.spirit = spirit;
}
Attack.prototype.start = function( type ){
return this.list[type].call(this);
}
Attack.prototype.list = {
waveBoxing: function(){
console.log( this.spirit.name + ':使用波动拳');
},
whirlKick: function(){
console.log( this.spirit.name + ':使用旋风腿');
}
}
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' );
spirit.attack( 'whirlKick' );
现在的Spirit精简了很多,不再包括各种攻击方法,而是把攻击动作委托给Attack类的对象来执行。
22.11 用return退出多重循环
var func = function() {
for( var i = 0; i < 10; i++){
for( var j = 0; j < 10; j++ ){
if( i * j > 30 ){
return;
}
}
}
console.log( i )
}
func();
以上代码在return退出整个方法之后,会导致 console.log(i) 没机会执行。
解决方案:
如果return 之后代码比较少,可以:
var func = function() {
for( var i = 0; i < 10; i++){
for( var j = 0; j < 10; j++ ){
if( i * j > 30 ){
return i;
}
}
}
}
console.log(func())
如果代码比较多,就可以:
var print = function( i ){
console.log( i )
}
var func = function() {
for( var i = 0; i < 10; i++){
for( var j = 0; j < 10; j++ ){
if( i * j > 30 ){
return print(i);
}
}
}
}
func();