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