你的 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 及更新的語法;

有時,代碼優雅是創建在犧牲代碼可讀性及性能之上的,魚與熊掌不可兼得,具體的實現方式仍是須要根據實際場景來作不一樣的取捨。

公衆號不定時分享我的在前端方面的學習經驗,歡迎關注。

相關文章
相關標籤/搜索