在 ES6 你會遇到的新功能


Posted by ai86109 on 2020-06-28

ES5? ES6?

常聽到的 ES 其實就是 ECMAScript 的縮寫,他是一個標準、規範,而 JavaScript 就是根據這個標準所實作出來的,ECMAScript 是 JS 開發的規範,而 ES5, ES6 就是他的版本。

ES6 在 2015 年發表,所以 ES6 又有人稱之為 ES2015。


差別在哪裡?

既然版本更新了,應該會有些新功能,就像 PS5 出了,總有些跟 PS4 不一樣的地方吧。

你可能會說,新功能我也不一定要用,為什麼需要學?

我會說,就算你不用但你也會碰到,碰到的時候總要知道他是什麼東東吧,所以多少看一下也不吃虧XD

所以以下就來講幾個常見的新功能。


宣告變數

先來從宣告變數講起,過往我們宣告變數都是用 var,現在有了新的選擇就是 let & const

第一個, const 的值無法被改變。

const 其實就是 constant(常數),也就是不變的數,他的值無法被改變。

const a = 10
a = 20

console.log 出來會印出 10,嗯,因為不會變很合理。

那再看看另一個例子

const b = {
    number: 10
}
b.number = 20

console.log 出來竟然會印出 20,這是為什麼?不是說值不會變嗎?

但其實不會變的是記憶體位置,如果不知道為什麼,或是這一塊不清楚,可以參考 Huli 大這篇:從博物館寄物櫃理解變數儲存模型

第二個,let & const 的作用域(scope)和 var 不同

var 在 function 裡作用域是整個 function,而其他兩個則是在 block { } 裡面。

function test(){
    if(10 >5){
        var a = 10
    }
    console.log(a)
}
test()

此時會印出 10,因為 var 的 scope 是整個 function,很合理。

但如果是 let 或是 const

function test(){
    if (10 > 5) {
        let a = 10
    }
    console.log(a)
}
test()

則會印出 a is not defined

另外,盡量使用 let or const,而非 var,畢竟作用域太大可能會造成一些問題。


Template literals

字串相加
ES5 的寫法

ary += '<li data-num="' + i + '">' + npb[i].team + '</li>’;

要利用 + 號去連接,除了不好寫之外,在很多引號的時候也不好讀。

ES6的寫法

ary += `<li data-num='${i}'>${npb[i].team}</li>`;

最外層引號改為``,裡面以 ${變數},取代原本要加號&小引號才能帶入變數的方法。

換行
ES5
在寫多行字串時,需要 +'\n' 才能換行

ES6
在寫多行字串時,直接換行即可,不用再寫 +'\n' 換行


解構(destructuring)

過去在 ES5 時,必須這樣做才能把裡面的數拉出來賦值

var arr = [1, 2, 3]

var first = arr[0]
var second = arr[1]
var third = arr[2]

console.log(first, second)

在 ES6,只要格式相同就可以直接賦值

var arr = [1, 2, 3]

var [first, second] = arr

console.log(first, second)

如果 var [first] = arr,那麼就只有第一個賦值。

同理 object 也是

var obj = {
    name: 'Tom',
    age: 28,
    country: 'taiwan'
}
var {name, age, country} = obj
console.log(country)

也可以解構再解構

var obj = {
    name: 'Tom',
    age: 28,
    country: 'taiwan'
        family: {
            father: 'Tim'
        }
}
var {family: {father}} = obj
console.log(father)

就會印出 Tim

另外,也可以用在 obj 帶入 function 時

function data({a, b}){
    console.log(a)
}
data({
    a: 1,
    b: 2
})

只要格式正確就可以使用。


展開運算子(Spread operator)

接著要講展開運算子(spread operator)

var arr1 = [1, 2, 3]
var arr2 = [4, 5, 6, arr1]
console.log(arr2)

會印出[ 4, 5, 6, [ 1, 2, 3 ] ]
但如果運用 ... 就可以把它展開

var arr1 = [1, 2, 3]
var arr2 = [4, 5, 6, ...arr1]
console.log(arr2)

會印出[ 4, 5, 6, 1, 2, 3 ]

除了 array,物件也可以使用

var obj1 = {
    a: 1,
    b: 2
}

var obj2 = {
    ...obj1
}

console.log(obj2)

會印出 { a: 1, b: 2 }

所以其實利用這個特性,我們就可以完成複製

var fourth = [4]
var arr1 = [1, 2, 3, fourth]
var arr2 = [...arr1]
console.log(arr2, arr1 === arr2, arr1[3] === arr2[3])

印出我們可以得到:[ 1, 2, 3, [ 4 ] ] false true

兩個印出的陣列會長一樣,但因為是複製過來的所以記憶體位置不同,但 arr1[3] & arr2[3] 是參考同一個記憶體位置也就是 fourth,所以是 true。


反向展開(Rest parameters)

另一個是很像 spread operator 的東西,就是 rest parameters(反向展開)
利用剛剛的例子來說明

var arr = [1, 2, 3]

var [first, …rest] = arr

console.log(first, rest)

印出來的東西就會是 1[ 2, 3 ]
...變數 只能放在最後,下面這個就會出現錯誤(Rest elemental must be last element)

var arr = [1, 2, 3]
var [first, ...rest, last] = arr

同理,物件也可以使用
另外這樣的用法會變成陣列的形式,所以可以用取陣列的方式取值。

function add(...args){
    console.log(args[0])
}
add(1, 2, 3)

加上預設值(Default parameters)

再來是一個滿實用的功能 default parameters(加上預設值)。
先給一個 function

function repeat(str, times){
    return str.repeat(times)
}
console.log(repeat('abc', 4))

這時候如果把傳進去的 4 拿掉,times 應該會收不到東西而變成 undefined,那要如何給一個預設值,在沒有傳進資料時,function 還是正常運作。

function repeat(str, times = 4){
    return str.repeat(times)
}
console.log(repeat('abc'))

那就是用 = 的方式加上預設值
同樣的,前面的 str 也可以加上預設值,當沒東西傳入時就會使用。

再給一個物件的例子

const obj = {
    b: 2
}
const {a = 1, b = 1} = obj
console.log(a, b)

結果會印出 1 和 2,除了物件和 function,也可以用在 array 上。


箭頭函式(Arrow function)

再來是和 function 有關的箭頭函式(arrow function)
先來看一段程式碼

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

console.log(
    arr.filter(function(value){
        return value > 1
    }).map(function(value){
        return value * 2
    })
)

那如果以箭頭函式去改寫會變怎樣(去掉 function,改在 value 後面加箭頭)
若參數只有一個,可以省略括號

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

console.log(
    arr.filter(value => {
        return value > 1
    }).map(value => {
        return value * 2
    })
)

如果 function 內只有執行一行,可以將大括號&return 也省略。

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

console.log(
    arr.filter(value => value > 1)
        .map(value => value * 2)
)

可讀性就變得高很多,其實就是我們說的語法糖(Syntactic sugar),但如果裡面要執行多行還是要保留大括號和 return。


導入導出

在前面有說過,要將別的地方的東西導入,會使用到 module.exports & require,這邊有其他方式可以用。
ES5
導出端

function double(n){
    return n * 2
}
module.exports = double

導入端

var doubleNumber = require('./index')

console.log(doubleNumber(3))

ES6
導出端(在要導出的東西前加 export

export function double(n){
    return n * 2
}
export const PI = 3.14

導入端(用 import 後面接大括號,裡面放要導入的名稱)

import {double, PI} from './index'
console.log(double(3))

那如果我導出端不想分別寫,想跟之前一樣 module.exports 合在一起寫呢?

function double(n){
    return n * 2
}
const PI = 3.14

export {
    double,
    PI
}

想改名導出可以嗎?

export {
    double as doubleNumber,
    PI
}

那導入時改名呢?

import {doubleNumber as a, PI} from './index'
console.log(a(3))

那如果要導入的東西很多,一個一個寫不是很沒效率嗎?
所以可以寫成這樣一次導入(*在程式語言通常指全部的意思)

import * as indexFunction from './index'

console.log(indexFunction.double(3))

差不多了,再來講最後一個 export default
導出端

export default function double(n){
    return n * 2
}
export const PI = 3.14

導入端

import double, {PI} from './index'
console.log(double(3))

所以可以看到差別只在於要不要加大括號而已。

補充:
這邊可以把 export default 想成這樣的實作

import {default as double} from './index'
import double from './index'

Babel

最後要講 Babel,因為前端發展的速度比瀏覽器快得多,所以當你用了一個新的語法,但瀏覽器或其他平台卻看不懂,就會需要用 babel 去將它轉成舊語法。

就像是 ES6/7/8 ⇒ babel ⇒ ES5

但要注意因為 Node.js 沒有支援這個語法,所以在 log 時要用 npx babel-node 檔案名.js
但在此之前要先安裝 babel,按照指示裝好後就可以使用 npx babel-node 這個指令了。

所以我們就來執行剛剛 import 這個新語法,但很奇怪,結果怎麼還是錯誤?
因為還沒安裝套件,要告訴 babel 你要把多新的語法轉成多舊的語法。

先安裝這個套件 npm install --save @babel/preset-env,安裝完套件要跟 babel 說你要用這個套件,所以先新增個檔案叫 touch .babelrc,打開他並加入內容。

{
 "presets": ["@babel/preset-env"]
}

裝好後就可以順利使用 npx babel-node 這個指令了。


總結

其實 ES6 新增的語法不只這些,另外還有像是 map, symbol 之類的,只是如果扯到那些就必須把其他東西講得更深入。
詳細語法可以參考:https://github.com/DrkSephy/es6-cheatsheet


#ES6 #ES5 #babel #let #const







Related Posts

7月23日 星期四

7月23日 星期四

Linkedin Java 檢定題庫 子類別複寫

Linkedin Java 檢定題庫 子類別複寫

[進階 js 02]  賦值

[進階 js 02] 賦值


Comments