如何用 es6+ 寫出優(yōu)雅的 js 代碼

兼容 IE 沃呢?不存在的好嗎。

其實(shí)使用新語法配合 babel 的轉(zhuǎn)碼饼酿,已經(jīng)可以解決這一些問題了吭敢。既然如此,那就多使用新語法去探索一下怎么更好的去寫代碼吧缭受。

下面分享個人開發(fā)中常用的 js 寫法技巧胁澳,希望對各位有所幫助。

使用 let / const

var 命令會發(fā)生”變量提升“現(xiàn)象米者,即變量可以在聲明之前使用韭畸,值為 undefined。這種現(xiàn)象多多少少是有些奇怪的塘雳。

個人認(rèn)為陆盘,對聲明的變量確定后面不會發(fā)生更改時,即使性能上沒有太大提升差異在败明,但使用 const, 代碼的可讀性也會增強(qiáng)很多隘马。

  • const 實(shí)際上保證的,并不是變量的值不得改動妻顶,而是變量指向的那個內(nèi)存地址所保存的數(shù)據(jù)不得改動酸员。
  • let 變量指向的內(nèi)存地址,保存的只是一個指向?qū)嶋H數(shù)據(jù)的指針

補(bǔ)充 const 定義的變量不是數(shù)據(jù)不可變讳嘱,而是保存的引用地址不能發(fā)生改變幔嗦。例子如下:

const person = { age: 22 }
person.age = 1

console.log(person.age ) // 1

詳情看 let 和 const 命令

解構(gòu)賦值

ES6 允許按照一定模式,從數(shù)組和對象中提取值沥潭,對變量進(jìn)行賦值邀泉,這被稱為解構(gòu)(Destructuring)。

好處就是:解決了訪問多層嵌套的對象或數(shù)組的命名,減少代碼量

聲明多個變量:

// 聲明變量
let age = 22
let name = 'guodada'
let sex = 1

// better
let [age, name, sex] = [22, 'guodada', 1]
console.log(age, name, sex) // 22, guodada, 1

使用在對象中:

const obj = {
  name: {
    firstName: 'guo',
    lastName: 'dada'
  }
}

// 提取變量
const firstName = obj.name.firstName
const lastName = obj.name.lastName

// better
const { firstName, lastName } = obj.name 

使用在函數(shù)中:

// 在參數(shù)中結(jié)構(gòu)賦值汇恤,獲取參數(shù), 當(dāng)參數(shù)多的使用時候十分方便
function Destructuring({ name, age }) {
  return { name, age } // 相當(dāng)于 { name: name, age: age } , 可以簡寫
}

const params = { name: 'guodada', age: 22 }
Destructuring(params)

更多用法見 變量的解構(gòu)賦值

ES6 允許在對象之中庞钢,直接寫變量。這時因谎,屬性名為變量名, 屬性值為變量的值基括。

function f(x, y) {
  return {x: x, y: y};
}

// better
function f(x, y) {
  return {x, y};
}
f(1, 2) // Object {x: 1, y: 2}

擴(kuò)展符的運(yùn)用

es6 擴(kuò)展符有很多用法,他可以使你的代碼更加簡潔财岔,易懂风皿。這里就舉例常用的用法

在對象中的用法:

let obj = {
  name: 'guodada',
  age: 22,
  sex: 1
}

// 復(fù)制對象。擴(kuò)展符為淺復(fù)制=宠怠M┛睢!
const copy = { ...obj }

// 修改對象屬性值(生成新對象) 相當(dāng)于 Object.assgin({}, obj, { age: 18 })
const newObj = { ...obj, age: 18 }

// 結(jié)合結(jié)構(gòu)賦值
let { sex, ...z } = obj
z // { name: 'guodada', age: 22 }

在數(shù)組中的用法:

const arr = [1, 2, 3]
const arr2 = [4, 5, 6, 4]

// 復(fù)制數(shù)組患朱。擴(kuò)展符為淺復(fù)制B沉拧!裁厅!
const newArr = [...arr] // ...[1, 2, 3] => 相當(dāng)于展開數(shù)組:1, 2, 3

// 合并數(shù)組
const conbineArr = [...arr, ...arr2]

// 結(jié)合求最大值函數(shù)
Math.max(...arr)

// 結(jié)合 Set 實(shí)現(xiàn)數(shù)組去重。注意:json 等對象數(shù)組不可用
[...new Set(arr2)] // [4, 5, 6]

擴(kuò)展符的其他用法請自行查資料侨艾。

數(shù)組用法

const arr = [1, 2, 3, 4]

Array.isArray(arr) // 判斷是否為數(shù)組

arr.includes(2) // true 判斷數(shù)組中是否包含某項

arr.findIndex(d => d === 3) // 2 找出第一個符合條件的數(shù)組成員并返回數(shù)組下標(biāo), 找不到返回 -1

arr.find(d => d === 3) // 3 找出第一個符合條件的數(shù)組成員并返回, 找不到返回 undefined

// es5 其他還有 filter map forEach 等执虹,這里不做舉例。
arr.every(d => d > 2) // false 每一項都滿足條件則返回 true

arr.some(d => d > 2) // true 只要有一項滿足條件則返回 true

find/findIndex : 找出第一個符合條件的數(shù)組成員之后不再匹配唠梨,一定程度下優(yōu)化查找袋励。
includes: 返回 true/false, 相較于 indexOf, 實(shí)用多了

  • flat() : 扁平化數(shù)組,常用于將數(shù)組轉(zhuǎn)化為一維數(shù)組

    const arr = [1, 2, [3, 4]]
    
    arr.flat() // [1, 2, 3, 4] 扁平化數(shù)組, 默認(rèn)展開一層当叭。
    
    const arr2 = [1, 2, [3, 4, [5, 6]]]
    
    arr2.flat() // [1, 2, 3, 4, [5, 6]]
    arr2.flat(2) // [1, 2, 3, 4, 5, 6] flat(3) 也是展開兩層...
    
  • flatMap(): 在數(shù)組執(zhí)行 map 方法后執(zhí)行 flat, 用的不多茬故,其實(shí)可以寫 map 后寫 flat 更好懂點(diǎn)。

    [2, 3, 4].flatMap(x => [x, x * 2]) //  [ 2, 4, 3, 6, 4, 8 ]
    // 1. [2, 3, 4].map(d => [d, d * 2]) => [[2, 4], [3, 6], [4, 8]]
    // 2. [[2, 4], [3, 6], [4, 8]].flat()
    

補(bǔ)充常用的對象轉(zhuǎn)數(shù)組的用法:

const obj = { name: 'guodada' }
  
Object.keys(obj) // ['name']
Object.values(obj) // ['guodada']
Object.entries(obj) // [['name', 'guodada']]

模板字符串

用的挺多的蚁鳖,注意不兼容 IE !

const name = 'guodada'

const newStr = `welcome ${name}` // welcome guodada

// the same as
const newStr = 'welcome ' + name

使用 async / await

async/await 實(shí)際上就是 generator 的語法糖, 主要用來解決異步問題磺芭,具體網(wǎng)上很多文章都有介紹,這里就不做多的解釋吧醉箕。

async function test() {
  const data = await axios.get('https://randomuser.me/api/')
  console.log(data)
}
// 等同于
function test() {
  axios.get('https://randomuser.me/api/').then(res => console.log(res)) // axios 也是 promise 對象
}

// 結(jié)合try/catch 
async function test() {
  try {
    const data = await axios.get('https://randomuser.me/api/')
    console.log(data)
  } catch (err) {
    console.log(err)
  }
}

ps 雖然好用钾腺,但是有時候適用場景不好,比如我們在拉取列表和用戶信息需要同時進(jìn)行時讥裤,await 后才執(zhí)行下一條語句放棒,這不是我們希望看到的。解決方法如下:

// 結(jié)合 Promise.all
const [result1, result2, result3] = await Promise.all([anAsyncCall(), thisIsAlsoAsync(), oneMore()])

傳送門:async 函數(shù)

利用 class 封裝代碼

主要是抽離代碼邏輯己英,使得代復(fù)用性加強(qiáng)间螟。同時,class 的形式會讓結(jié)構(gòu)變得更加清晰,譬如:

class MyForm {
  /**
   * @func defaultLimit - 默認(rèn)表單輸入限制條件, value 為空時返回 true
   * @param {Number} type - 代表表單類型的節(jié)點(diǎn)厢破!
   * @param {String} value - 需要被驗證的值
   * @return Boolean
   * 
   * 根據(jù) type 屬性對輸出進(jìn)行驗證
   * 1 0≤x≤50 整數(shù)
   * 2 -1000≤x≤2000 整數(shù)
   * 3 1≤x 整數(shù)
   * 4 0≤x≤10
   */
  static defaultLimit(type, value) {
    const typeLimitMap = {
      1: /^(\d|[1-4]\d|50)$/g,
      2: /^-?(\d{1,3}|1000)$|^(-|1\d{3}|2000)$/,
      3: /^[1-9]\d*$/,
      4: value => value <= 10 && value >= 0 // 0≤ x ≤ 10 可以為小數(shù)
    }
    if (!typeLimitMap[type] || !value) return true
    if (typeof typeLimitMap[type] === 'function') return typeLimitMap[type](value)
    else return typeLimitMap[type].test(value)
  }

  /**
   * @func translateLimit - 轉(zhuǎn)換操作符
   * @param {String} operator - 運(yùn)算符
   * @param {*} value - 被匹配的值
   * @param {*} compareValue - 匹配的值
   * @return Boolean
   * 'eq': '='
   * 'ne': '≠'
   * 'gt': '>'
   * 'lt': '<'
   * 'ge': '≥'
   * 'le': '≤'
   */
  static translateLimit(operator, value, compareValue) {
    const type = {
      eq: value === compareValue,
      ne: value !== compareValue,
      gt: value > compareValue,
      lt: value < compareValue,
      ge: value >= compareValue,
      le: value <= compareValue
    }
    if (!Object.keys(type).includes(operator) || !value || value === '-') return true
    return type[operator]
  }

  // ...
}

export default MyForm

使用:

import MyForm from './MyForm'

MyForm.defaultLimit(1, 20)
  • static :靜態(tài)屬性邮府,類可以直接調(diào)用
  • constructor : 實(shí)例化類的時候調(diào)用,即 new MyForm(), 這里沒用到

更多知識請閱 Class 的基本語法

優(yōu)化 if/else 語句

當(dāng)邏輯或||時溉奕,找到為 true 的分項就停止處理褂傀,并返回該分項的值,否則執(zhí)行完加勤,并返回最后分項的值仙辟。

當(dāng)邏輯與&&時,找到為 false 的分項就停止處理鳄梅,并返回該分項的值叠国。

const a = 0 || null || 3 || 4
console.log(a) // 3

const b = 3 && 4 && null && 0
console.log(b) // null

減少 if / else地獄般的調(diào)用

const [age, name, sex] = [22, 'guodada', 1]

if (age > 10) {
  if (name === 'guodada') {
    if (sex > 0) {
      console.log('all right')
    }
  }
}

// better 使用 &&
if (age > 10 && name === 'guodada' && sex > 0) {
  console.log('all right')
}

// 或者(太長了不推薦)
age > 10 && name === 'guodada' && sex > 0 && console.log('all right')

提一下 react 的坑點(diǎn), 在 render

render(){
  const arr = []
  return arr.length && null
}
// 渲染出 0 !
// Boolean / undefind / null / NaN 等才不會渲染戴尸。我們可以使用 !! 強(qiáng)制轉(zhuǎn)化為 boolean 解決這個問題
return !!arr.length && null

// 使用 && 控制組件的渲染
this.state.visible && <Modal />

使用 Array.includes 來處理多重條件:

const ages = [18, 20, 12]

if (age === 18 || age === 12) {
  console.log('match')
}

// better
if ([18, 12].includes(age)) {
  console.log('match')
}

如果是較少的判斷邏輯則可以使用三元運(yùn)算符:

const age = 22
const isAdult = age >= 18 ? true : false // 這里可以寫為 const isAdult = age > 18

const type = age >= 18 ? 'adult' : 'child'

優(yōu)化 switch/case 語句

switch/caseif/else 代碼結(jié)構(gòu)好點(diǎn)粟焊,但也和它一樣有時十分冗長。

這里以自己實(shí)際項目中代碼舉例:
有時我們可能需要對不同類型的字段進(jìn)行不一樣的正則驗證孙蒙,防止用戶錯誤地輸入项棠。譬如

const [type, value] = [1, '20']
/**
 * 根據(jù) type 屬性對輸出進(jìn)行驗證
 * 1 0≤x≤50 整數(shù)
 * 2 -1000≤x≤2000 整數(shù)
 * 3 1≤x 整數(shù)
 */

function func1(type, value) {
  if (type === 1) {
    return /^(\d|[1-4]\d|50)$/.test(value)
  } else if (type === 2) {
    return /^-?(\d{1,3}|1000)$|^(-|1\d{3}|2000)$/.test(value)
  } else if (type === 3) {
    return /^[1-9]\d*$/.test(value)
  } else {
    return true
  }
}

func1(type, value)

// 使用 switch/case
function fun2(type, value) {
  switch (type) {
    case 1:
      return /^(\d|[1-4]\d|50)$/.test(value)
    case 2:
      return /^-?(\d{1,3}|1000)$|^(-|1\d{3}|2000)$/.test(value)
    case 3:
      return /^[1-9]\d*$/.test(value)
    default:
      return true
  }
}

func2(type, value)

我們?nèi)绾吻擅畹慕鉀Q這個代碼冗長的問題呢,如下:

function func3(type, value) {
  const limitMap = {
    1: /^(\d|[1-4]\d|50)$/g,
    2: /^-?(\d{1,3}|1000)$|^(-|1\d{3}|2000)$/,
    3: /^[1-9]\d*$/
  }
  return limitMap[type].test(value)
}

利用對象去匹配屬性值挎峦,可以減少你的代碼量香追,也使你的代碼看起來更加簡潔。你也可以使用 Map 對象去匹配坦胶。

function func4(type, value) {
  const mapArr = [
    [1, /^(\d|[1-4]\d|50)$/g],
    [2, /^-?(\d{1,3}|1000)$|^(-|1\d{3}|2000)$/],
    [3, /^[1-9]\d*$/]
  ]
  const limitMap = new Map(mapArr)
  return limitMap.get(type).test(value)
}

Map 是一種鍵值對的數(shù)據(jù)結(jié)構(gòu)對象透典,它的匹配更加嚴(yán)格。它會區(qū)分開你傳遞的是字符串還是數(shù)字顿苇,譬如:

limitMap.get(1) // /^(\d|[1-4]\d|50)$/g
limitMap.get('1') // undefined

更多詳見 Set 和 Map 數(shù)據(jù)結(jié)構(gòu)

其他

  • 函數(shù)參數(shù)默認(rèn)值
    function func(name, age = 22) {}
    // 等同于
    function func(name, age) {
      age = age || 22
    }
    
  • 使用 === 代替 ==峭咒。其實(shí)大家都懂這個的。纪岁。凑队。
  • 箭頭函數(shù),es6 最常用的語法蜂科。
  • return boolean
    const a = 1
    return a === 1 ? true : false
    // 多此一舉了顽决,其實(shí)就等于
    return a === 1
    

敬請各位補(bǔ)充。交流才能進(jìn)步导匣,相視一笑才菠,嘿嘿。

  • github - star 一下人生更美好
  • blog - 歡迎交流
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贡定,一起剝皮案震驚了整個濱河市赋访,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖蚓耽,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件渠牲,死亡現(xiàn)場離奇詭異,居然都是意外死亡步悠,警方通過查閱死者的電腦和手機(jī)签杈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鼎兽,“玉大人答姥,你說我怎么就攤上這事⊙枰В” “怎么了鹦付?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長择卦。 經(jīng)常有香客問我敲长,道長,這世上最難降的妖魔是什么秉继? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任祈噪,我火速辦了婚禮,結(jié)果婚禮上秕噪,老公的妹妹穿的比我還像新娘钳降。我一直安慰自己,他們只是感情好腌巾,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著铲觉,像睡著了一般澈蝙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上撵幽,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天灯荧,我揣著相機(jī)與錄音,去河邊找鬼盐杂。 笑死逗载,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的链烈。 我是一名探鬼主播厉斟,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼强衡!你這毒婦竟也來了擦秽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎感挥,沒想到半個月后缩搅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡触幼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年硼瓣,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片置谦。...
    茶點(diǎn)故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡堂鲤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出霉祸,到底是詐尸還是另有隱情筑累,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布丝蹭,位于F島的核電站慢宗,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏奔穿。R本人自食惡果不足惜镜沽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贱田。 院中可真熱鬧缅茉,春花似錦、人聲如沸男摧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽耗拓。三九已至拇颅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間乔询,已是汗流浹背樟插。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留竿刁,地道東北人黄锤。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像食拜,于是被迫代替她去往敵國和親鸵熟。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,103評論 1 32
  • 一监婶、ES6簡介 ? 歷時將近6年的時間來制定的新 ECMAScript 標(biāo)準(zhǔn) ECMAScript 6(亦稱 ...
    一歲一枯榮_閱讀 6,078評論 8 25
  • [TOC] 參考阮一峰的ECMAScript 6 入門參考深入淺出ES6 let和const let和const都...
    郭子web閱讀 1,781評論 0 1
  • 學(xué)習(xí)了核聚老師的記憶的神經(jīng)認(rèn)知原理,知道學(xué)習(xí)就是記憶煮盼,記憶的本質(zhì)就是大腦神經(jīng)通路短纵、回路的改變,就如同我們鍛煉身體僵控,...
    學(xué)習(xí)生活閱讀 631評論 2 2
  • 抬頭望紅霞下映襯得有些發(fā)紅色的男子香到,洛顏突然覺得自己是個啞巴也不錯,索性不再開口报破。 “啞巴悠就,看你對著池子里的...
    淺色風(fēng)景123閱讀 391評論 0 2