代码是写给人看的 精简的代码让人心情愉悦 优秀的产品离不开程序猿精心雕琢
将查询函数和修改函数分离 将查询动作从修改动作中分离出来的方式
如果遇到一个“既有返回值又有副作用”的函数,此时可以将查询动作从修改动作中分离出来。
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 31 32 33 34 function alertForMiscreant (people) { for (const p of people) { if (p === "Don" ) { setOffAlarms (); return "Don" ; } if (p === "John" ) { setOffAlarms (); return "John" ;} } return "" ; }const found = alertForMiscreant (people);function findMiscreant (people) { for (const p of people) { if (p === "Don" ) { return "Don" ; } if (p === "John" ) { return "John" ; } } return "" ; }function alertForMiscreant (people) { if (findMiscreant (people) !== "" ) setOffAlarms (); }const found = findMiscreant (people);alertForMiscreant (people);
以卫语句取代嵌套条件表达式 如果某个条件极其罕见,就应该单独检查该条件,并在该条件为真时立刻从函数中返回。 这样的单独检查常常被称为“卫语句”(guard clauses)。
如果使用if-else结构,你对if分支和else分支的重视是同等的。这样的代码结构传递给阅读者的消息就是:各个分支有同样的重要性。卫语句就不同了,它告诉阅读者: “这种情况不是本函数的核心逻辑所关心的, 如果它真发生了,请做一些必要的整理工作,然后退出。” 为了传递这种信息可以使用卫语句替换嵌套结构。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function payAmount (employee ) { let result; if (employee.isSeparated ) { result = {amount : 0 , reasonCode :"SEP" }; } else { if (employee.isRetired ) { result = {amount : 0 , reasonCode : "RET" }; } else { result = someFinalComputation (); } } return result; }function payAmount (employee ) { if (employee.isSeparated ) return {amount : 0 , reasonCode : "SEP" }; if (employee.isRetired ) return {amount : 0 , reasonCode : "RET" }; return someFinalComputation (); }
分解条件表达式 将条件表达式提炼成函数
在带有复杂条件逻辑的函数中,往往可以将原函数中对应的代码改为调用新函数。 对于条件逻辑, 将每个分支条件分解成新函数可以带来的好处:
提高可读性
可以突出条件逻辑, 更清楚地表明每个分支的作用
突出每个分支的原因
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 if (!aDate.isBefore (plan.summerStart ) && !aDate.isAfter (plan.summerEnd )) charge = quantity * plan.summerRate ;else charge = quantity * plan.regularRate + plan.regularServiceCharge ;if (summer ()) charge = summerCharge ();else charge = regularCharge ();function summer ( ) { return !aDate.isBefore (plan.summerStart ) && !aDate.isAfter (plan.summerEnd ); }function summerCharge ( ) { return quantity * plan.summerRate ; }function regularCharge ( ) { return quantity * plan.regularRate + plan.regularServiceCharge ; } charge = summer () ? summerCharge () : regularCharge ();
拆分循环 将一个循环拆分成多个循环
当遇到一个身兼数职的循环时可以将循环拆解,让一个循环只做一件事情, 那就能确保每次修改时你只需要理解要修改的那块代码的行为就可以了。该行为可能会被质疑,因为它会迫使你执行两次甚至多次循环,实际情况是,即使处理的列表数据更多一些,循环本身也很少成为性能瓶颈,更何况拆分出循环来通常还使一些更强大的优化手段变得可能。
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 const people = [ { age : 20 , salary : 10000 }, { age : 21 , salary : 15000 }, { age : 22 , salary : 18000 } ]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 ; }console .log (`youngestAge: ${youngest} , totalSalary: ${totalSalary} ` );const people = [ { age : 20 , salary : 10000 }, { age : 21 , salary : 15000 }, { age : 22 , salary : 18000 } ]let totalSalary = 0 ;for (const p of people) { totalSalary += p.salary ; }let youngest = people[0 ] ? people[0 ].age : Infinity ;for (const p of people) { if (p.age < youngest) youngest = p.age ; } console .log (`youngestAge: ${youngest} , totalSalary: ${totalSalary} ` );const people = [ { age : 20 , salary : 10000 }, { age : 21 , salary : 15000 }, { age : 22 , salary : 18000 } ]console .log (`youngestAge: ${youngestAge()} , totalSalary: ${totalSalary()} ` );function totalSalary ( ) { let totalSalary = 0 ; for (const p of people) { totalSalary += p.salary ; } return totalSalary; } function youngestAge ( ) { let youngest = people[0 ] ? people[0 ].age : Infinity ; for (const p of people) { if (p.age < youngest) youngest = p.age ; } return youngest; }const people = [ { age : 20 , salary : 10000 }, { age : 21 , salary : 15000 }, { age : 22 , salary : 18000 } ]console .log (`youngestAge: ${youngestAge()} , totalSalary: ${totalSalary()} ` );function totalSalary ( ) { return people.reduce ((total,p ) => total + p.salary , 0 ); }function youngestAge ( ) { return Math .min (...people.map (p => p.age )); }
提炼函数、函数参数化、使用策略模式替换 if-else、switch-case 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 function getPrice (tag, originPrice ) { if (tag === 'newUser' ) { return originPrice > 50.1 ? originPrice - 50 : originPrice } if (tag === 'back' ) { return originPrice > 200 ? originPrice - 50 : originPrice } if (tag === 'activity' ) { return originPrice > 300 ? originPrice - 100 : originPrice } }const priceHandler = { newUser (originPrice ){ return originPrice > 50.1 ? originPrice - 50 : originPrice }, back (originPrice ){ return originPrice > 200 ? originPrice - 50 : originPrice }, activity (originPrice ){ return originPrice > 300 ? originPrice - 100 : originPrice } }function getPrice (tag, originPrice ){ return priceHandler[tag](originPrice) }
封装变量 将变量封装起来,只允许通过函数访问
对于所有可变的数据, 只要它的作用域超出单个函数,就可以采用封装变量的方法。数据被使用得越广, 就越是值得花精力给它一个体面的封装。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let defaultOwner = {firstName : "Martin" , lastName : "Fowler" }; spaceship.owner = defaultOwner; defaultOwner = {firstName : "Rebecca" , lastName : "Parsons" };function getDefaultOwner ( ) {return defaultOwner;}function setDefaultOwner (arg ) {defaultOwner = arg;} spaceship.owner = getDefaultOwner ();setDefaultOwner ({firstName : "Rebecca" , lastName : "Parsons" });
多个条件判断 数组includes方法用来判断是否包含某个元素,如果是返回 true,否则false。
1 2 3 4 5 6 7 8 9 if (student === "Tom" || student === "Jack" || student === "Shanguagua" ) { } if (['Tom' , 'Jack' , 'Shanguagua' ].includes (student)) { }
if … else 缩写 使用三元表达式替代
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let win;let score = 70 ; if (score > 100 ) { win = true ; } else { win = false ; } let win = (score > 100 ) ? true : false ;let win = score > 100 ;
多个三元表达式建议拆开写
声明变量 如果需要声明两个具有共同值或共同类型的变量时,试一下这种写法。
1 2 3 4 5 6 let Tom = 1 ;let Jack = 1 ; let Tom , Jack = 1 ;
多个变量赋值 当我们需要给不同的变量赋不同值的时候,这个方法就有点给力了,代码之美体现的淋漓尽致。
1 2 3 4 5 6 7 8 var Tom , Jack , Shanguagaua ;Tom = 'A' ;Jack = 'B' ;Shanguagaua = 'C' ; let [Tom , Jack , Shanguagau ] = ['A' , 'B' , 'C' ];
多个条件的AND(&&)运算符 如果要在true的情况下再执行其它操作,if是一种方法,但是AND运算符逼格稍高。
1 2 3 4 5 6 7 if (ready) { goToEat (); } ready && goToEat ();
箭头函数 箭头函数是ES6的特性,简化了函数的写法,还有其他特性。
1 2 3 4 5 6 7 function GoToEat (a, b ) { return a + b; } const GoToEat = (a, b ) => a + b;
短函数调用 根据条件判断两个函数的调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function A ( ) { console .log ("A" ); }function B ( ) { console .log ("B" ); } var c = 5 ;if (c == 10 ) { A (); } else { B (); } (c === 1 ? A : B)();
Switch速记 根据条件判断多个函数的调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 switch (key) { case 1 : A (); break ; case 2 : B (); break ; case 3 : C (); break ; } var data = { 1 : A, 2 : B, 3 : C }; data[something] && data[something]();
因为某个值如果不存在对象中会得到假值,反之真值,再结合AND运算符就可以了。
默认参数值 利用函数的默认值特性,可以避免校验假值操作。
1 2 3 4 5 6 7 8 9 10 function GoToEat (A, B ) { if (A === undefined ) A = 1 ; if (B === undefined ) B = 2 ; return A + B; } GoToEat = (A = 1 , B = 2 ) => A + B;GoToEat ();
点运算符 数组的拼接可以使用点运算符来完成。
1 2 3 4 5 6 7 const A = [1 , 2 , 3 ];const B = [4 , 5 , 6 ].concat (A); const A = [1 , 2 , 3 ];const B = [4 , 5 , 6 , ...A];
当然拷贝一个数组也可以用点运算符,注意是深拷贝。
1 2 3 4 5 6 7 var A = [1 , 2 , 3 ];var B = A.slice (); var A = [1 , 2 , 3 ];var B = [...A];
Array.find的简写 当我们有一个对象数组并且我们想要根据对象属性查找特定对象时,find方法确实很有用,这是数据过滤时很常用的操作。
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 const data = [ { type : "student" , name : "Tom" , }, { type : "teacher" , name : "Mick" , }, { type : "student" , name : "Shanguagua" , }, ];function findStudent (name ) { for (let i = 0 ; i < data.length ; ++i) { if (data[i].type === "student" && data[i].name === name) { return data[i]; } } } filterdData = data.find ( (data ) => data.type === "student" && data.name === "Shanguagua" );
重复一个字符串多次 要一次又一次地重复相同的字符,我们可以使用for循环并将它们添加到同一循环中。重复的操作总会有简洁的写法。
1 2 3 4 5 6 7 8 9 let Tom = '' ; for (let i = 0 ; i < 5 ; i ++) { Tom += 'Tom ' ; } console .log (str); 'Tom ' .repeat (5 );
在数组中查找最大值和最小值 咋一看,额,用for循环吧,哦,不是的。
1 2 3 const arr = [1 , 2 , 3 ]; Math .max (…arr); Math .min (…arr);
指数幂简化
双重按位操作 1 2 3 4 5 Math .floor (1.9 ) === 1 ~~1.9 === 1
将字符串转成数字 1 2 3 4 5 6 7 8 9 10 11 let test1 = parseInt ('123' ); let test2 = parseFloat ('12.3' ); let test1 = +'123' ; let test2 = +'12.3' ;new Date ().getTime ()Date .now () +new Date ()
参考资料 https://juejin.cn/post/7131211363493347335 https://juejin.cn/post/6956844713479520286 https://juejin.cn/post/7078575191244144670