你的 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 代码本能够更加优雅。测试

三目运算符

若是经过逻辑判断只是单纯为了进行赋值的操做,那么咱们一般可使用三目运算符来解决。ui

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

是否看起来更加温馨了呢。spa

Object / Map 对象

基本上全部的逻辑判断操做均可以经过 Object / Map 对象的方式来解决。prototype

// 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 对象更能体现出它们的优点。code

让咱们扩展上面的栗子,在经过 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 及更新的语法;

有时,代码优雅是创建在牺牲代码可读性及性能之上的,鱼与熊掌不可兼得,具体的实现方式仍是须要根据实际场景来作不一样的取舍。

公众号不定时分享我的在前端方面的学习经验,欢迎关注。

相关文章
相关标签/搜索