你的 JS 代码本可以更加优雅

有时感觉挺有趣的是在群里聊天时的自嘲,「xx 项目在经过我一年的不断努力下,终于变得不可维护」。个人认为,维护是一件比开发更富挑战性的事情,前人的代码是否规范优雅会很直接地影响我们的工作效率和心情。

所以,我们更要时刻地去注意我们代码的质量,也许你的代码已经足够规范,但在某种程度上来讲却不够优雅。本文列出了一些让 JS 代码更加优雅的技巧及建议,希望能够对你有所帮助。

我的世界不只有 if else

在逻辑判断的场景中,经常我们的第一反应都是使用 if else / switch 来解决,因为这是最符合我们命令式逻辑思维的语法(难道不是因为书里只教了这两个吗)。

但当在我们的逻辑判断场景中有很多种情况需要判断时,使用 if else / switch 固然能够解决我们的问题,但却让人感觉代码比较冗余而不够优雅。

举个栗子,通过 type 类型来判断相应的学生类型。

// if else
let getStudentType = (type) =>{
  if(type == 1){
    console.log('学神')
  } else if (type == 2){
    console.log('学霸')
  } else if (type == 3) {
    console.log('学渣')
  } else {
    console.log('学灰')
  }
}
// switch
let getStudentType = (type) => {
  switch (type) {
    case 1:
      console.log('学神')
      break
    case 2:
      console.log('学霸')
      break
    case 3:
      console.log('学渣')
      break
    default:
      console.log('学灰')
      break
  }
}

如上,通过 if else / switch 语法虽然能够直观地表现出代码的逻辑,但却似乎让人感觉有些重复赘余。其实,对于逻辑判断的场景,我们还可以有更多其它的方式来解决;其实,你的 JS 代码本可以更加优雅。

三目运算符

如果通过逻辑判断只是单纯为了进行赋值的操作,那么我们通常可以使用三目运算符来解决。

let getStudentType = (type) => {
  let studentType = type == 1 ?'学神'
    : type == 2 ? '学霸'
      : type == 3 ? '学渣' : '学灰';
  console.log(studentType)
}

是否看起来更加舒适了呢。

Object / Map 对象

基本上所有的逻辑判断操作都可以通过 Object / Map 对象的方式来解决。

// Object 对象
let getStudentType = (type) => {
  let obj = {
    1:'学神',
    2:'学霸',
    3:'学渣',
    0:'学灰'
  }
  let studentType = obj[type] || obj['0'];
  console.log(studentType);
}
// Map 对象
let getStudentType = (type) => {
  let map = new Map([
    [1, '学神'],
    [2, '学霸'],
    [3, '学渣'],
    [0, '学灰']
  ])
  let studentType = map.get(type) || map.get('0');
  console.log(studentType);
}

在逻辑判断的场景中通过 Object / Map 对象我们依旧能够实现优雅的代码,当然,上面所举的栗子仅仅只是逻辑判断中进行赋值操作的场景,在对于需要做更多操作的逻辑判断的场景中,Object / Map 对象更能体现出它们的优势。

让我们扩展上面的栗子,在通过 type 类型判断相应学生类型之后,每个学生还会发动自身相关类型的技能。

通过 if else 实现的方式如下

// if else
let studentAction = (type) =>{
  if(type == 1){
    console.log('学神')
    launchXueshenSkill();
  } else if (type == 2){
    console.log('学霸')
    launchXuebaSkill();
  } else if (type == 3) {
    console.log('学渣')
    launchXuezhaSkill();
  } else {
    console.log('学灰')
    launchXuehuiSkill();
  }
}

而通过 Object / Map 对象,可以更优雅地实现

// Object 对象
let getStudentType = (type) => {
  let obj = {
    1: () => { console.log('学神'); launchXueshenSkill(); },
    2: () => { console.log('学霸'); launchXuebaSkill(); },
    3: () => { console.log('学渣'); launchXuezhaSkill(); },
    0: () => { console.log('学灰'); launchXuehuiSkill(); },
  }
  let studentSkill = obj[type] || obj['0'];
  studentSkill();
}
// Map 对象
let getStudentType = (type) => {
  let map = new Map([
    [1, () => { console.log('学神'); launchXueshenSkill(); }],
    [2, () => { console.log('学霸'); launchXuebaSkill(); }],
    [3, () => { console.log('学渣'); launchXuezhaSkill(); }],
    [0, () => { console.log('学灰'); launchXuehuiSkill(); }]
  ])
  let studentSkill = map.get(type) || map.get('0');
  studentSkill()
}

Object 和 Map 对象都能解决所有的逻辑判断的问题,那么它们两者有什么区别呢?

Map 对象是 ES6 中的语法,它与 Object 对象最大的区别就是 Object 对象的键只能是字符串,而 Map 对象的键可以是任意值。

所以相对而言,Map 对象较 Object 对象更加灵活,在更加复杂的逻辑判断中,当我们的键使用字符串不再满足需求时,使用 Map 对象才能实现我们的目的。

true && xxx

true && xxx 主要是适用于 if else 中一些简单场景的情况。判断一个值是否为 true,如果为 true 时则执行 xxx 的相关操作。

let isGoodStudent = true;
// if else
if (isGoodStudent){
  console.log('我是一个好学生')
}
// true && xxx 
isGoodStudent && (console.log('我是一个好学生'));

三行的代码最终简写为一行,真是优雅呀!

false || variable

false || xxx 的使用场景是作为某些场景下三目运算符的简洁写法。判断一个变量是否存在,若是不存在(为 false )则赋值另一个变量(variable)。

let studentType1 = '学神'
let studentType2 = '学霸'
// 三目运算符
let goodStudent = studentType1 ? studentType1 : studentType2;
// false || xxx
let goodStudent = studentType1 || studentType2;
我的世界不只有 for

在逻辑循环的场景中,经常我们的第一反应都是使用 for / while 来解决,因为这也是最符合我们命令式逻辑思维的语法(难道不还是因为书里只教了这两个吗)。

但与 if else / switch 一样,for / while 也是比较直观但同时欠缺优雅性的写法。

let studentType = ['学神', '学霸', '学渣', '学灰'];
// for
for (let i = 0, len = studentType.length; i < len; i++) {
  console.log(studentType[i]);
}
// while
let i = 0;
while (i < studentType.length){
  console.log(studentType[i]);
  i++;
}

同样的,对于逻辑循环的场景,我们还可以有更多其它的方式来解决。

Array.prototype.forEach

forEach() 方法的使用场景与 for / while 基本是一致的(forEach 循环不能提前终止),只要是逻辑循环的场景,都可以使用 forEach() 来实现。

studentType.forEach((v,i) => {
  console.log(v);
})
Array.prototype.map

map() 方法若是只需要取得数组的元素进行循环的一些操作,则其使用方式与 forEach() 是一致的。

studentType.map((v, i) => {
  console.log(v);
})

map() 方法会返回一个新数组,其结果是原始数组中的每个元素都调用一个提供的函数后返回的结果。

举个栗子,在 studentType 类型中的每个元素后面都添加 +10086 的字符串然后返回一个新数组。

llet superStudentType = studentType.map((v, i) => `${v}+10086`)
console.log(superStudentType); // [ '学神+10086', '学霸+10086', '学渣+10086', '学灰+10086' ]

所以,map() 方法除了能代替 for / while 循环外,还提供了对原始数组元素操作并返回新数组的功能(这同样也可以使用 for / while 循环来实现,只是需要书写更多的代码来实现)。

同样的,下述所列举的关于数组的方法,都是可以通过 for / while 循环来实现的,只是使用下述已封装好的方法,会让我们的代码逻辑更清晰并且代码更加简洁优雅。

Array.prototype.filter

filter() 方法返回一个新数组, 其包含通过所提供函数实现的测试的所有元素。

let studentTypeWithScore = [{
    type:'学神',
    score:100
  },{
    type: '学霸',
    score: 85
  },{
    type: '学渣',
    score: 65
  },{
    type: '学灰',
    score: 50
  }]
let goodStudentType = studentTypeWithScore.filter((v, i) => v.score > 80)
console.log(goodStudentType); // [ { type: '学神', score: 100 }, { type: '学霸', score: 85 } ]
Array.prototype.find

find() 方法返回数组中满足提供的测试函数的第一个元素的值,否则返回 undefined。

所以,当我们只想获得数组中符合条件的第一个元素时,使用 find() 方法会比使用 filter() 方法更加高效简洁。find() 方法获得符合条件的第一个元素时就停止遍历了,而 filter() 方法需要遍历数组全部元素获得符合条件的所有元素并取出第一个元素。

let oneGoodStudentType = studentTypeWithScore.find((v, i) => v.score > 80)
console.log(oneGoodStudentType); // { type: '学神', score: 100 }
Array.prototype.some

some() 方法用于检测数组中是否有元素满足指定条件。

同样的,当我们只想确定数组中是否有符合条件的元素,使用 some() 方法会比使用 find() 方法更加高效简洁。some() 方法是返回布尔值而 find() 方法是返回符合条件的第一个元素值。

let hasGoodStudentType = studentTypeWithScore.some((v, i) => v.score > 80)
console.log(hasGoodStudentType); // true
Array.prototype.every

every() 方法测试数组的所有元素是否都通过了指定函数的测试。

let isAllGoodStudentType = studentTypeWithScore.every((v, i) => v.score > 80)
console.log(isAllGoodStudentType); // false
let isAllStudentType = studentTypeWithScore.every((v, i) => v.score > 40)
console.log(isAllStudentType); // true
Array.prototype.reduce

reduce() 方法对累计器和数组中的每个元素(从左到右)应用一个函数,将其简化为单个值。

let sum = studentTypeWithScore.reduce((acc, curVal) => acc + curVal.score, 0); // 100 + 85 + 65 + 50
console.log(sum); // 300
其它技巧及建议
  1. 当仅仅是为了判断字符串中是否存在某子串时,使用 String.prototype.includes 代替 String.prototype.indexOf;
  2. 当仅仅是为了判断数组中是否存在某元素时,使用 Array.prototype.includes 代替 Array.prototype.indexOf;
  3. 尽可能地减少代码块的嵌套;
  4. 尽可能使用 ES6 及更新的语法;

有时,代码优雅是建立在牺牲代码可读性及性能之上的,鱼与熊掌不可兼得,具体的实现方式还是需要根据实际场景来做不同的取舍。

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