1.基础重构1.提取函数1.1使用场景如果你需要花时间浏览一段代码来弄清楚它在做什么,那么你应该将它提取成一个函数并根据它的作用命名它.1.2使用steps新建一个函数,根据这个函数的用途命名,将源函数中要提取的代码复制到新建的目标函数中。仔细检查提取的代码以查找对作用域为源函数且在新提取的函数中不可访问的变量的引用。如果是这样,将它们作为参数传递给新函数。处理完所有变量后,进行编译。在源函数中,提取的代码段被替换为对目标函数的调用。测试。查看其他代码是否与提取的代码段相同或相似。如果是这样,请考虑将内联代码替换为调用提取的新函数的函数调用。1.3范例functionprintOwing(invoice){letoutstanding=0;//计算未付金额(constoofinvoice.orders){outstanding+=o.amount;}//记录截止日期consttoday=Clock.today;invoice.dueDate=newDate(today.getFullYear(),today.getMonth(),today.getDate()+30);console.log(`name:${invoice.customer}`);console.log(`金额:${outstanding}`);console.log(`due:${invoice.dueDate.toLocaleDateString()}`);}//重构造functionprintOwing(invoice){letoutstanding=calculateOutstanding(invoice)recordDueDate(invoice)printDetails(invoice,outstanding)}functioncalculateOutstanding(invoice){letoutstanding=0;for(constoofinvoice.orders){outstanding+=o.amount;}returnoutstanding;}functionrecordDueDate(invoice){consttoday=Clock.today;invoice.dueDate=newDate(today.getFullYear(),today.getMonth(),today.getDate()+30);}functionprintDetails(invoice,outstanding){console.log(`name:${invoice.customer}`);console.log(`金额:${outstanding}`);console.log(`due:${invoice.dueDate.toLocaleDateString()}`);}2。InlineFunction2.1使用场景如果代码中的间接层太多,以至于系统中的每个函数似乎都是对另一个函数的简单委托,让我在这些委托动作之间头晕目眩,那么我通常会使用Inlinefunctions2.2使用步骤检查函数以确保它不是多态的。找到这个函数的所有调用点。用函数体替换这个函数的所有调用点。每次更换后,进行测试。删除函数的定义。2.3示例函数reportLines(aCustomer){constlines=[];收集客户数据(线路,客户);返回行;}functiongatherCustomerData(out,aCustomer){out.push(["name",aCustomer.name]);出去。推送([“位置”,aCustomer.location]);}//重构函数reportLines(aCustomer){constlines=[];lines.push(["姓名",aCustomer.name]);lines.push(["location",aCustomer.location]);returnlines;}3.ExtractVariable3.1UsageScenario表达式可能非常复杂且难以阅读。3.2使用步骤确认待精化的表达式没有副作用。声明一个不可修改的变量,复制你要提取的表达式,并用表达式的结果值赋值给变量。用这个新变量替换原来的表达式。测试。3.3示例在一个函数中,将它们细化为变量functionprice(order){returnorder.quantity*order.itemPrice-Math.max(0,order.quantity-500)*order.itemPrice*0.05+Math.min(order.quantity*order.itemPrice*0.1,100);}//重构函数price(order){constbasePrice=order.quantity*order.itemPrice;constquantityDiscount=Math.max(0,order.quantity-500)*order.itemPrice*0.05;constshipping=Math.min(basePrice*0.1,100);returnbasePrice-quantityDiscount+shipping;}在一个类中,将它们提炼成方法classOrder{constructor(aRecord){this._data=aRecord;}getquantity(){returnthis._data.quantity;}getitemPrice(){returnthis._data.itemPrice;}getprice(){returnthis.quantity*this.itemPrice-Math.max(0,this.quantity-500)*this.itemPrice*0.05+Math.min(this.quantity*this.itemPrice*0.1,100);}}//重构类Order{constructor(aRecord){this._data=aRecord;}getquantity(){返回this._data.quan实体;}getitemPrice(){returnthis._data.itemPrice;}getprice(){returnthis.basePrice-this.quantityDiscount+this.shipping;}getbasePrice(){returnthis.quantity*this.itemPrice;getquantityDiscount(){returnMath.max(0,this.quantity-500)*this.itemPrice*0.05;}getshipping(){returnMath.min(this.basePrice*0.1,100);}}4。InsideInlineVariable4.1使用场景有时,变量并不比表达式本身更具表现力,这可能会阻碍附近代码的重构。如果是这种情况,则应通过内联消除该变量。4.2使用步骤检查变量赋值语句的右侧表达式是否有副作用。如果变量没有被声明为不可变的,先让它不可变,然后进行测试。找到变量的第一次使用,将其替换为直接使用赋值语句的右侧表达式。测试。重复前面两步,将其他所有使用该变量的地方一一替换。删除变量的声明点和赋值语句。测试。4.3例子letbasePrice=anOrder.basePrice;return(basePrice>1000);//重构returnanOrder.basePrice>1000;5.改变函数声明5.1使用场景如果我有一个函数,函数参数的名字你一眼就看不出它的用途。一旦找到它,您必须尽快重命名它。5.2使用步骤首先写一段注释描述这个函数的用途。然后把这个注释变成函数名。先完成函数重命名。测试。然后添加参数。测试。5.3示例函数inNewEngland(aCustomer){return["MA","CT","ME","VT","NH","RI"].includes(aCustomer.address.state);}constnewEnglanders=someCustomers.filter(c=>inNewEngland(c));//重构函数inNewEngland(stateCode){return["MA","CT","ME","VT","NH","RI"].includes(stateCode);}constnewEnglanders=someCustomers.filter(c=>inNewEngland(c.address.state));6.封装变量6.1使用场景对于所有可变数据,只要其作用域超出单个函数,就进行封装,只允许通过函数访问。6.2使用步骤创建包装函数,在其中访问和更新变量值。执行静态检查。一个一个地修改使用变量来调用适当的包装函数的代码。每次更换后,进行测试。限制变量的可见性。测试。如果变量的值是一个记录,可以考虑使用封装记录6.3例子7.变量重命名(RenameVariable)7.1使用场景变量为了能够很好的解释一个程序在做什么,对于范围超出函数调用的字段,您需要更改它们用心命名。7.2使用步骤如果变量被广泛使用,可以考虑用wrapper变量封装。找出所有使用这个变量的代码,一一修改。测试。7.3实例8.引入参数对象(IntroduceParameterObject)8.1使用场景变量为了能够很好地说明程序在做什么,对于超出函数调用范围的字段,需要仔细命名。8.2使用步骤如果您还没有合适的数据结构,请创建一个。测试。使用更改函数声明为原函数添加一个新参数,其类型为新创建的数据结构。测试。调整所有调用者以传入新数据结构的适当实例。每次进行修改时,都会执行测试。用新数据结构中的每一个元素逐一替换参数列表中对应的参数项,然后删除原来的参数。测试。8.3示例conststation={name:"ZB1",readings:[{temp:47,time:"2016-11-1009:10"},{temp:53,time:"2016-11-1009:20"},{temp:58,time:"2016-11-1009:30"},{temp:53,time:"2016-11-1009:40"},{temp:51,time:"2016-11-1009:50"},]};functionreadingsOutsideRange(station,min,max){returnstation.readings.filter(r=>r.temp
