第二十二章 代码重构

以下都是一些建议,没有哪些是必须严格遵守的标准。
具体是否需要重构,以及如何进行重构,这需要我们根据系统的类型、项目工期、人力等外界因素一起决定。

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();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,192评论 6 511
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,858评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,517评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,148评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,162评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,905评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,537评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,439评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,956评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,083评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,218评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,899评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,565评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,093评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,201评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,539评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,215评论 2 358

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,654评论 18 399
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy阅读 9,521评论 1 51
  • 说到茶道,我是个地地道道的门外汉,也是在红菇的影响下,第一次接触了茶文化,第一次了解了红茶的故事,感触颇深。 人们...
    林星儿阅读 404评论 0 4
  • 有的时候,你看一部电视剧或者电影,也许只为了看到那一句话而已罢了。关于电影里的剧情和人物你可能早已经忘记。但是那句...
    邓小怪阅读 454评论 0 0
  • 二十岁的你,能干什么?二十岁的你或许在上大学,每天睡觉学习玩手机?二十岁我们充满稚嫩但又初步接触到这个社会的烟尘之...
    柒89阅读 334评论 0 0