theme: github
封装
封装记录
用数据类取代记录型结构
1
const organization = { name:'Acme', country: "GB" }
1
2
3
4
5
6
7
8class Organization {
constructor(data){
this._name = data.name
}
get name(){ return this._name }
set name(name){ this._name = name }
}
const organization = new Organization({...})做法
- 对持有记录的变量使用封装变量,将其封装在一个函数中
- 创建一个类,将记录包装起来,并将记录变量的只替换成该类的一个实例,然后在类上定义访问和修改的函数
- 新建一个函数,让它返回该类的对象,而非原始记录
- 替换项目中的使用点
1
2
3
4
5
6
7const organization = { name:'Acme', country: "GB" }
let result = ''
result += `<h1>${organization.name} : ${organization.country}</h1>\n`
organization.name = '121'
organization.country = '121'
result += `<h1>${organization.name} : ${organization.country}</h1>`
console.log(result)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class Organization {
constructor(data){
this._name = data.name
this._country = data.country
}
get name(){ return this._name }
get country(){ return this._country }
set name(name){ this._name = name }
set country(country){ this._country = country }
}
const organization = new Organization({ name:'Acme', country: "GB" })
let result = ''
result += `<h1>${organization.name} : ${organization.country}</h1>\n`
organization.name = '121'
organization.country = '121'
result += `<h1>${organization.name} : ${organization.country}</h1>`
console.log(result)封装集合
对可变数据进行封装,不返回源数据
1
2
3
4
5
6
7class Person {
get courses() { return this._courses }
set courses(courses) { this._courses = courses }
}
class Course {
...
}1
2
3
4
5
6
7
8
9class Person {
get courses() { return this._courses.slice() }
set courses(courses) { this._courses = courses.slice() }
addCourse(aCourse) { ... }
removeCourse(aCourse) { ... }
}
class Course {
...
}做法
- 如果集合对引用尚未封装,则先用封装变量封装
- 在类上增加用于增删对函数
- 查找集合的引用点,修改后测试
- 每次只返回一份只读的副本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21class Person {
constructor(name){
this._name = name
this._courses = []
}
get name(){return this._name }
get courses() { return this._courses }
set courses(courses) { this._courses = courses }
}
class Course {
constructor(name, isAdvanced){
this._name = name
this._isAdvanced = isAdvanced
}
get name(){return this._name }
get isAdvanced(){return this._isAdvanced }
}
const CoursesName = [{ name:'Math', isAdvanced: true }, { name:'Chinese', isAdvanced: true }, { name:'English', isAdvanced: false }]
const p = new Person('xy')
p.courses = CoursesName.map(({ name, isAdvanced }) => new Course(name, isAdvanced))
p.courses.push(new Course('history', true)) // 无法监测的行为!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29class Person {
constructor(name){
this._name = name
this._courses = []
}
get name(){return this._name }
get courses() { return this._courses.slice() }
set courses(courses) { this._courses = courses.slice() }
addCourse(aCourse) { this._courses.push(aCourse) }
removeCourse(aCourse){
const index = this._courses.indexOf(aCourse)
if(index === -1) throw new RangeError("not in")
this._courses.splice(index, 1)
}
}
class Course {
constructor(name, isAdvanced){
this._name = name
this._isAdvanced = isAdvanced
}
get name(){return this._name }
get isAdvanced(){return this._isAdvanced }
}
const CoursesName = [{ name:'Math', isAdvanced: true }, { name:'Chinese', isAdvanced: true }, { name:'English', isAdvanced: false }]
const history = new Course('history', true)
const p = new Person('xy')
p.courses = CoursesName.map(({ name, isAdvanced }) => new Course(name, isAdvanced))
p.addCourse(history)
p.removeCourse(history)以对象取代基本类型
当基本数据类型不满足业务需求时,将其封装成类方便管理
1
2
3
4class Order{
get priority(){return this._priority }
}
const priorities = ['low', 'normal', 'high', 'rush']1
2
3
4
5
6
7
8
9
10class Order{
constructor(data){
this._priority = new Priority()
}
}
class Priority{
toString(){return this._value}
static legalValues(){return ['low', 'normal', 'high', 'rush']}
}
const priorities = Priority.legalValues()做法
- 如果变量未被封装起来,则先封装变量
- 为数据值创建一个简单的类,保存数据并提供取值函数
- 修改第一步得到的设值函数,令其创建一个新类的对象将其存入字段
- 修改取值函数,令其调用新类的取值函数
1
2
3
4
5
6
7
8
9class Order{
constructor(data){
this._priority = data.priority
}
get priority(){return this._priority }
}
const priorities = ['low', 'normal', 'high', 'rush']
let orders = new Array(10).fill(0).map((item, index) => new Order({ priority: priorities[index % 4] }))
console.log(orders.filter(item => item.priority === 'high' || item.priority === 'rush'))1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29class Order{
constructor(data){
this._priority = new Priority(data.priority)
}
get priorityString(){return this._priority.toString() }
get priority(){return this._priority}
set priority(aString){this._priority = new Priority(aString)}
}
class Priority{
constructor(value){
if(value instanceof Priority) return value
if(Priority.legalValues().includes(value)){
this._value = value
}else{
throw new Error('invalid for Priority')
}
}
toString(){return this._value}
static legalValues(){return ['low', 'normal', 'high', 'rush']}
get _index(){ return Priority.legalValues().findIndex(item => item === this._value) }
equals(order) { return this._index === order._index }
higherThan(order) { return this._index > order._index }
lessThan(order) { return this._index < order._index }
}
const priorities = Priority.legalValues()
const normal = new Priority('normal')
let orders = new Array(10).fill(0).map((item, index) => new Order({ priority: priorities[index % 4] }))
console.log(orders.filter(item => item.priority.higherThan(normal)))以查询取代临时变量
将临时变量中的计算逻辑移动到类/函数中,通过查询获取
1
2
3
4
5
6
7class Order {
get price() {
let basePrice = ...
let discountFactor = ...
return basePrice * discountFactor
}
}1
2
3
4
5class Order {
get price() {...}
get basePrice() { ... }
get discountFactor() {...}
}做法
- 检查变量在是否完全计算完毕,是否存在副作用
- 如果变量不是只读的,可以先改造成只读变量
- 将变量赋值代码提炼成设值函数
1
2
3
4
5
6
7
8
9
10
11
12class Order {
constructor(quantity, item){
this._quantity = quantity
this._item = item
}
get price() {
let basePrice = this._quantity * this._item.price
let discountFactor = 0.98
if(basePrice > 1000) discountFactor -= 0.03
return basePrice * discountFactor
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class Order {
constructor(quantity, item){
this._quantity = quantity
this._item = item
}
get price() {
return this.basePrice * this.discountFactor
}
get basePrice() { return this._quantity * this._item.price }
get discountFactor() {
let discountFactor = 0.98
if(this.basePrice > 1000) discountFactor -= 0.03
return discountFactor
}
}提炼类(内联类)
将较大的类中的模块分离出去成为独立的类
1
2
3class Person {
...
}1
2
3
4
5
6
7
8class Person {
constructor(name){
this._telephoneNumber = new TelephoneNumber()
}
}
class TelephoneNumber{
...
}做法
- 决定如何分解类所负的责任
- 创建一个新的类,用以表现从旧类中分离出来的责任
- 构造旧类时创建一个新类的实例,建立联系
- 搬移字段,函数,去掉不再需要的函数接口
1
2
3
4
5
6
7
8
9
10
11class Person {
constructor(name){
this._name = name
}
get name(){return this._name }
get telephoneNumber(){return `(${this.officeAreaCode}) ${this.officeNumber}` }
get officeAreaCode(){return this._officeAreaCode }
set officeAreaCode(arg){return this._officeAreaCode = arg }
get officeNumber(){return this._officeNumber }
set officeNumber(arg){return this._officeNumber = arg }
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19class Person {
constructor(name){
this._name = name
this._telephoneNumber = new TelephoneNumber()
}
get name(){return this._name }
get telephoneNumber(){return this._telephoneNumber.toString() }
get officeAreaCode(){return this._telephoneNumber.areaCode }
set officeAreaCode(arg){return this._telephoneNumber.areaCode = arg }
get officeNumber(){return this._telephoneNumber.number }
set officeNumber(arg){return this._telephoneNumber.number = arg }
}
class TelephoneNumber{
get number(){return this._number}
set number(arg){return this._number = arg}
get areaCode(){return this._areaCode }
set areaCode(arg){return this._areaCode = arg}
toString(){return `(${this._areaCode}) ${this._number}`}
}内联类(提炼类)
当一个类不再承担足够的责任时,将其直接内联到引用其的类中
1
2
3
4
5
6
7
8class Shipment {
constructor(){
this._trackingInformation = new TrackingInformation(company, number)
}
}
class TrackingInformation {
...
}1
2
3
4
5
6class Shipment {
constructor(){
...
}
... //TrackingInformation
}做法
- 对于内联类中的所有函数,在目标类中创建对应的函数
- 修改源类函数所有的引用点,令其调用目标类对应的委托方法
- 将所有的函数数据移动到目标类中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19class TrackingInformation {
constructor(company, number){
this._shippingCompany = company
this._trackingNumber = number
}
get shippingCompany(){return this._shippingCompany}
set shippingCompany(arg) {this._shippingCompany = arg}
get trackingNumber(){return this._trackingNumber}
set trackingNumber(arg) {this._trackingNumber = arg}
get display(){ return `${this.shippingCompany} ${this.trackingNumber}` }
}
class Shipment {
constructor(name, company, number){
this._name = name
this._trackingInformation = new TrackingInformation(company, number)
}
get trackingInfo(){return this._trackingInformation.display}
get trackingInformation(){return this._trackingInformation}
}1
2
3
4
5
6
7
8
9
10
11
12
13class Shipment {
constructor(name, company, number){
this._name = name
this._shippingCompany = company
this._trackingNumber = number
}
get shippingCompany(){return this._shippingCompany}
set shippingCompany(arg) {this._shippingCompany = arg}
get trackingNumber(){return this._trackingNumber}
set trackingNumber(arg) {this._trackingNumber = arg}
get trackingInfo(){ return `${this.shippingCompany} ${this.trackingNumber}` }
get trackingInformation(){return { _shippingCompany: this.shippingCompany, _trackingNumber: this._trackingNumber}}
}隐藏委托关系(移除中间人)
隐藏跨级引用的情况,增加快速访问的接口
1
2
3
4
5
6class Person {
get department(){return this._department }
}
class Department {
get chargeCode(){return this._chargeCode }
}1
2
3
4
5
6class Person {
get manager(){return this._department.manager }
}
class Department {
get manager(){return this._manager}
}做法
- 对于每个委托关系的函数,在服务对象端建立一个简单的委托函数
- 调整客户端,令其调用服务对象提供的函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14class Person {
constructor(name){
this._name = name;
}
get name(){return this._name }
get department(){return this._department }
set department(arg){this._department = arg}
}
class Department {
get chargeCode(){return this._chargeCode}
set chargeCode(arg){this._chargeCode = arg}
get manager(){return this._manager}
set manager(arg){this._manager = arg}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14class Person {
constructor(name){
this._name = name;
}
get name(){return this._name }
set department(arg){this._department = arg}
get manager(){return this._department.manager }
}
class Department {
get chargeCode(){return this._chargeCode}
set chargeCode(arg){this._chargeCode = arg}
get manager(){return this._manager}
set manager(arg){this._manager = arg}
}移除中间人(隐藏委托关系)
类中存在大量的委托函数,服务类变成类一个中间人
1
2
3
4
5
6class Person {
get manager(){return this._department.manager }
}
class Department {
get manager(){return this._manager}
}1
2
3
4
5
6class Person {
get department(){return this._department }
}
class Department {
get chargeCode(){return this._chargeCode }
}做法
- 为受托对象创建一个取值函数
- 对于每个委托函数,是客户端转为连续的调用访问
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class Person {
constructor(name){
this._name = name;
}
get name(){return this._name }
set department(arg){this._department = arg}
get manager(){return this._department.manager }
get chargeCode(){return this._department.chargeCode}
}
class Department {
get chargeCode(){return this._chargeCode}
set chargeCode(arg){this._chargeCode = arg}
get manager(){return this._manager}
set manager(arg){this._manager = arg}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14class Person {
constructor(name){
this._name = name;
}
get name(){return this._name }
get department(){return this._department }
set department(arg){this._department = arg}
}
class Department {
get chargeCode(){return this._chargeCode}
set chargeCode(arg){this._chargeCode = arg}
get manager(){return this._manager}
set manager(arg){this._manager = arg}
}替换算法
使用更清晰的方式替换复杂的方式
1
2
3
4
5
6function findPerson(people){
for(let i = 0; i < people.length; i++){
if(people[i] === 'Don') return 'Don'
}
return ''
}1
2
3function findPerson(people) {
return people.find(p => ['Don'].includes(p)) || ''
}做法
- 整理代替换的算法,确保它以及被抽取到一个独立的函数中
- 为函数准备测试,固定其行为
- 准备好另一个算法进行替换
1
2
3
4
5
6
7
8function findPerson(people){
for(let i = 0; i < people.length; i++){
if(people[i] === 'Don') return 'Don'
if(people[i] === 'Join') return 'Join'
if(people[i] === 'Kent') return 'Kent'
}
return ''
}1
2
3
4function findPerson(people) {
const candidates = ['Don', 'Join', 'Kent']
return people.find(p => candidates.includes(p)) || ''
}
搬移特性
搬移函数
合理的把函数放置在它应该出现的位置
1
2
3
4function trackSummary(points){
...
function distance(p1, p2){...}
}1
2function trackSummary(points){ ... }
function distance(p1, p2){...}做法
- 检查函数在当前上下文引用的元素,考虑是否将他们一起搬移
- 检查待搬移函数是否具有多态性
- 将函数复制一份到目标的上下文中,调整函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31function trackSummary(points){
const totalTime = calculateTime()
const totalDistance = calculateDistance()
const pace = totalTime / 60 / totalDistance
return {
time: totalTime,
distance: totalDistance,
pace
}
function calculateDistance(){
let result = 0
for(let i = 1; i < points.length; i++){
result += distance(points[i - 1], points[i])
}
return result
}
function distance(p1, p2){
const EARTH_RADIUS = 3959
const dLat = radians(p2.lat) - radians(p1.lat)
const dLon = radians(p2.lon) - radians(p1.lon)
const a = Math.pow(Math.sin(dLat / 2), 2) + Math.cos(radians(p2.lat)) * Math.cos(radians(p1.lat)) * Math.pow(Math.sin(dLon / 2), 2)
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
return EARTH_RADIUS * c
}
function radians(degrees){
return degrees * Math.PI / 180
}
function calculateTime(){
return 6
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31function trackSummary(points){
const totalTime = calculateTime()
const distance = totalDistance(points)
const pace = totalTime / 60 / distance
return {
time: totalTime,
distance,
pace
}
function calculateTime(){
return 6
}
}
function totalDistance(points){
let result = 0
for(let i = 1; i < points.length; i++){
result += distance(points[i - 1], points[i])
}
return result
}
function distance(p1, p2){
const EARTH_RADIUS = 3959
const dLat = radians(p2.lat) - radians(p1.lat)
const dLon = radians(p2.lon) - radians(p1.lon)
const a = Math.pow(Math.sin(dLat / 2), 2) + Math.cos(radians(p2.lat)) * Math.cos(radians(p1.lat)) * Math.pow(Math.sin(dLon / 2), 2)
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
return EARTH_RADIUS * c
}
function radians(degrees){
return degrees * Math.PI / 180
}搬移字段
如果更新一个字段,需要同时在几个结构中进行修改,则表明该字段需要被搬移到一个集中的地点
1
2
3
4
5
6
7class Customer {
constructor(name, discountRate){
this._discountRate = discountRate
this._contact = new Contact()
}
}
class Contact { ... }1
2
3
4
5
6
7
8
9
10class Customer {
constructor(name, discountRate){
this._contact = new Contact(discountRate)
}
}
class Contact {
constructor(discountRate){
this._discountRate = discountRate
}
}做法
- 确保源字段已经得到良好封装
- 在目标对象上创建一个字段
- 确保源对象能正常引用目标对象
- 调整源对象的访问函数,令其使用目标对象的字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19class Customer {
constructor(name, discountRate){
this._name = name
this._discountRate = discountRate
this._contact = new Contact()
}
get discountRate(){return this._discountRate}
becomePreferred(){
return this._discountRate += 0.03
}
applyDiscount(amount){
return amount - amount * this.discountRate
}
}
class Contact {
constructor(){
this._startDate = new Date()
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22class Customer {
constructor(name, discountRate){
this._name = name
this._contact = new Contact(discountRate)
}
get discountRate(){return this._contact.discountRate}
_setDiscountRate(aNumber){return this._contact.discountRate = aNumber}
becomePreferred(){
return this._setDiscountRate(this._contact.discountRate += 0.03)
}
applyDiscount(amount){
return amount - amount * this.discountRate
}
}
class Contact {
constructor(discountRate){
this._startDate = new Date()
this._discountRate = discountRate
}
get discountRate(){return this._discountRate}
set discountRate(arg){this._discountRate = arg}
}搬移语句到函数(搬移语句到调用者)
某些语句与一个函数放在一起更加像一个整体的时候,就可以将其搬移到函数中
1
2
3
4
5
6
7
8
9
10function renderPerson(person){
[`<p>title: ${aPhoto.title}</p>`, ...emitPhotoData(person.photo)]
}
function emitPhotoData(aPhoto){
return [`<p>date: ${aPhoto.date}</p>`]
}
function renderPhoto(aPhoto){
return [`<p>title: ${aPhoto.title}</p>`,
`<p>date: ${aPhoto.date}</p>`].join('/n')
}1
2
3
4
5
6
7
8
9function renderPerson(person){
[...emitPhotoData(person.photo)]
}
function emitPhotoData(aPhoto){
return [`<p>title: ${aPhoto.title}</p>`, `<p>date: ${aPhoto.date}</p>`]
}
function renderPhoto(aPhoto){
return [...emitPhotoData(person.photo)].join('/n')
}做法
- 如果重复的代码距离目标函数有些距离,先用移动语句
- 如果目标函数仅被唯一一个源函数调用,则只需要将源函数中重复的代码段剪切复制到函数中
- 如果由多个调用点,则先选择对一个调用点应用提炼函数,将待搬移语句与目标函数提炼成一个新函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28function renderPerson(person){
const result = []
result.push(`<p>${person.name}</p>`)
result.push(renderPhoto(person.photo))
result.push(`<p>title: ${person.photo.title}</p>`)
result.push(emitPhotoData(person.photo))
return result.join('\n')
}
function emitPhotoData(aPhoto){
const result = []
result.push(`<p>location: ${aPhoto.location}</p>`)
result.push(`<p>date: ${aPhoto.date.toDateString()}</p>`)
return result.join('\n')
}
function renderPhoto(aPhoto){
const result = []
result.push(`<p>name: ${aPhoto.name}</p>`)
result.push(`<p>color: ${aPhoto.color}</p>`)
return result.join('\n')
}
function renderDiv(person){
return [
"<div>",
`<p>title: ${person.photo.title}</p>`,
emitPhotoData(person.photo),
"</div>"
].join('\n')
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24function renderPerson(person){
const result = []
result.push(`<p>${person.name}</p>`)
result.push(renderPhoto(person.photo))
result.push(emitPhotoData(person.photo))
return result.join('\n')
}
function renderPhoto(aPhoto){
const result = []
result.push(`<p>name: ${aPhoto.name}</p>`)
result.push(`<p>color: ${aPhoto.color}</p>`)
return result.join('\n')
}
function renderDiv(person){
const result = ["<div>", emitPhotoData(person.photo), "</div>"]
return result.join('\n')
}
function emitPhotoData(aPhoto) {
return [
`<p>title: ${aPhoto.title}</p>`,
`<p>location: ${aPhoto.location}</p>`,
`<p>date: ${aPhoto.date.toDateString()}</p>`
].join('\n')
}搬移语句到调用者(搬移语句到函数)
当函数边界发生偏移,在某些调用点表现出不同的行为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21function renderPerson(person){
[...emitPhotoData(person.photo)]
}
function emitPhotoData(aPhoto){
return [`<p>title: ${aPhoto.title}</p>`, `<p>date: ${aPhoto.date}</p>`]
}
function renderPhoto(aPhoto){
return [...emitPhotoData(person.photo)].slice(1).join('/n')
}
```
```js
function renderPerson(person){
[`<p>title: ${aPhoto.title}</p>`, ...emitPhotoData(person.photo)]
}
function emitPhotoData(aPhoto){
return [`<p>date: ${aPhoto.date}</p>`]
}
function renderPhoto(aPhoto){
return [...emitPhotoData(aPhoto)].join('/n')
}做法
- 如果源函数很简单,且调用者也不多,则只需把要搬移的代码复制回去
- 若调用点不止一个,则先用提炼函数把不想搬移的代码提炼成一个新函数
- 对源函数应用内联函数,对提炼函数进行修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24function renderPerson(person){
const result = []
result.push(`<p>${person.name}</p>`)
result.push(renderPhoto(person.photo))
result.push(emitPhotoData(person.photo))
return result.join('\n')
}
function renderPhoto(aPhoto){
const result = []
result.push(`<p>name: ${aPhoto.name}</p>`)
result.push(`<p>color: ${aPhoto.color}</p>`)
return result.join('\n')
}
function renderDiv(person){
const result = ["<div>", emitPhotoData(person.photo), "</div>"]
return result.join('\n')
}
function emitPhotoData(aPhoto) {
return [
`<p>title: ${aPhoto.title}</p>`,
`<p>location: ${aPhoto.location}</p>`,
`<p>date: ${aPhoto.date.toDateString()}</p>`
].join('\n')
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27function renderPerson(person){
const result = []
result.push(`<p>${person.name}</p>`)
result.push(renderPhoto(person.photo))
result.push(emitPhotoData(person.photo))
result.push(`<p>location: ${person.photo.location}</p>`)
return result.join('\n')
}
function renderPhoto(aPhoto){
const result = []
result.push(`<p>name: ${aPhoto.name}</p>`)
result.push(`<p>color: ${aPhoto.color}</p>`)
return result.join('\n')
}
function renderDiv(person){
const result = ["<div>",
emitPhotoData(person.photo),
`<p>location: ${person.photo.location}</p>`,
"</div>"]
return result.join('\n')
}
function emitPhotoData(aPhoto) {
return [
`<p>title: ${aPhoto.title}</p>`,
`<p>date: ${aPhoto.date.toDateString()}</p>`
].join('\n')
}函数调用取代内联代码
通过函数的方式去消除重复的逻辑
1
2
3
4
5let appliesToMass = false;
let states = ['MA']
for(let s of states) {
if(s === 'MA') appliesToMass = true;
}1
2let states = ['MA']
let appliesToMass = states.includes('MA');做法
- 内联代码替代为对一个既有函数的调用
1
2
3
4
5let appliesToMass = false;
let states = ['MA']
for(let s of states) {
if(s === 'MA') appliesToMass = true;
}1
2let states = ['MA']
let appliesToMass = states.includes('MA');移动语句
让存在关联的代码一起出现,使代码更加容易理解1
2
3
4
5
6
7if(stack.length === 0){
...
stack.push(i)
}else{
...
stack.push(i)
}1
2
3
4
5
6if(stack.length === 0){
...
}else{
...
}
stack.push(i)做法
- 确定待移动的代码片段应该搬完何处,搬移后是否会影响正常工作
- 搬移代码
1
2
3
4
5
6
7
8
9
10let result, stack = []
for(let i = 0; i < 5; i++){
if(stack.length === 0){
result = 0
stack.push(i)
}else{
result = stack[stack.length - 1]
stack.push(i)
}
}1
2
3
4
5
6
7
8
9let result, stack = []
for(let i = 0; i < 5; i++){
if(stack.length === 0){
result = 0
}else{
result = stack[stack.length - 1]
}
stack.push(i)
}拆分循环
拆分身兼多职的循环,让代码更加易读
1
2
3
4for (const p of people) {
if(p.age < youngest) youngest = p.age
totalSalary += p.salary
}1
2let youngest = Math.min(...people.map(p => p.age))
let totalSalary = people.reduce((prev, item) => prev + item.salary, 0)做法
- 复制一遍循环代码
- 识别移除循环中的重复代码,使得每个循环只做一件事
- 拆分后使用提炼函数
1
2
3
4
5
6
7
8
9
10
11
12
13const people = [
{ age: 20, salary: 10000 },
{ age: 30, salary: 10000 },
{ age: 25, salary: 20000 },
{ age: 22, salary: 50000 },
{ age: 26, salary: 60000 },
]
let youngest = people[0] ? people[0].age : Infinity;
let totalSalary = 0
for (const p of people) {
if(p.age < youngest) youngest = p.age
totalSalary += p.salary
}1
2let youngest = Math.min(...people.map(p => p.age))
let totalSalary = people.reduce((prev, item) => prev + item.salary, 0)管道取代循环
使用管道优化迭代结构,可读性更强
1
2
3
4
5function acquireData(input) {
for (const line of lines) {
...
}
}1
2
3function acquireData(input) {
lines.slice(1).filter(line => line.trim() !== '')...
}做法
- 创建一个新变量,用于存放和参与循环过程的集合
- 从顶部开始,将循环每一块行为都搬移出来,在上一步创建的集合变量用一种管道运算替代
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28const input = `office, country, telephone
Chicago, USA, +1 312 373 1000
Beijing, China, +86 4000 900 505
Chicago, USA, +1 312 373 1000
Beijing, China, +86 4000 900 505
Chicago, USA, +1 312 373 1000
Beijing, China, +86 4000 900 505`;
function acquireData(input) {
const lines = input.split('\n')
let firstLine = true
const result = []
for (const line of lines) {
if(firstLine) {
firstLine = false
continue
}
if(line.trim() === '') continue
const record = line.split(',')
if(record[1].trim() === 'China'){
result.push({
city: record[0].trim(),
phone: record[2].trim(),
})
}
}
return result
}1
2
3
4
5
6
7
8
9
10
11
12function acquireData(input) {
const lines = input.split('\n')
return lines
.slice(1)
.filter(line => line.trim() !== '')
.map(line => line.split(','))
.filter(record => record[1].trim() === 'China')
.map(record => ({
city: record[0].trim(),
phone: record[2].trim(),
}))
}移除死代码
没用的东西大胆删除了他,不然越来越多