Babel Coder

พื้นฐาน ES2015 (ES6) สำหรับการเขียน JavaScript สมัยใหม่

beginner

ยุคของ ES2015 มาถึงแล้ว ผมเชื่อว่าเพื่อนๆหลายคนน่าจะกำลังใช้ ES2015 ในการเขียน JavaScript ปัจจุบันอยู่ สำหรับบทความนี้ผมจะแนะนำการใช้ ES2015 ในแบบฉบับที่เพื่อนๆจะได้พบเห็นในโลกของ JavaScript สมัยใหม่ พร้อมทั้งแนะนำจุดบอดหรือมุมมองที่เพื่อนๆอาจยังไม่เข้าใจหรือใช้ผิดกันอยู่ อย่าเสียเวลาแล้วลุยกันเลย!

ลาก่อน semicolon เพื่อนยาก

สำหรับ ES2015 นั้นไม่มีความจำเป็นที่คุณต้องใส่ semicolon หรือ ; อีกต่อไป ที่ผมพูดว่าไม่ต้องใส่นี้ไม่ได้หมายความว่า JavaScript เป็นภาษาที่สลัด semicolon ทิ้งเสียแล้ว แท้ที่จริงนั้น JavaScript ยังต้องการ semicolon อยู่ เพียงแต่ ES2015 นั้นฉลาดพอที่จะเติม semicolon ให้เราอัตโนมัติ (automatic semicolon insertion) ไร้ซึ่ง semicolon โค๊ดของเราจึงดูสวยขึ้น นี่ไม่ใช่สิ่งมหัศจรรย์ถ้าคุณไปเห็นโค๊ดของไลบรารี่ของใครซักคนในโลก React ที่ไม่ปิดท้ายด้วย ;

// โค๊ดที่เปลือยเปล่าไร้ semicolon ของฟังก์ชัน applyMiddleware ใน Redux
export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, initialState, enhancer) => {
    var store = createStore(reducer, initialState, enhancer)
    var dispatch = store.dispatch
    var chain = []

    var middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    }
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

หมายเหตุ: ASI มีมาก่อน ES6 ออก แต่ สมัย ES5 แม้เราจะละ semicolon ออกได้ แต่เราก็จะไม่ทำกันนั่นเพราะเครื่องมือหลายตัวจะทำเอาเจ๊งถ้าเอา semicolon ออก เช่นพวก compressors

การมาของ ES6 มาพร้อมกับเครื่องมือที่ทำให้ความหนักใจของการเอา semicolon ออกลดลง จนเราเริ่มนิยมเอา semicolon ออกสำหรับโค้ด ES6 และยังคง semicolon ไว้สำหรับโค้ด ES5 ครับ

จาก var สู่ let และ const

ผมว่าเพื่อนๆคงเริ่มใช้ทั้ง let และ const กันแล้ว แต่ทราบหรือไม่ว่า let/const นั้นมีขอบเขตการมีตัวตนหรือ scope ที่แตกต่างจาก var กล่าวคือ var นั้นเป็น function-scoped หรือพูดง่ายๆคือเราใช้ var ประกาศตัวแปรตรงจุดไหนก็ตาม แต่เมื่อ JavaScript เริ่มทำงาน การประกาศตัวแปรด้วย var นั้นจะกระดึ๊บๆตัวเองไปอยู่ใกล้กับจุดประกาศฟังก์ชันที่ใกล้ที่สุดหรือที่เราเรียกกันว่า hoisting

function foo(isValid) {
  if(isValid) {
    var x = 1
    return x
  }
  
  return x
}

// มีค่าเท่ากับ
function foo(isValid) {
  var x; // ดีดตัวเองมาอยู่ใกล้จุดประกาศฟังก์ชัน
  if(isValid) {
    var x = 1
    return x
  }
  
  return x
}

สำหรับ let และ const นั้นเป็น block-scoped หมายถึงประกาศตัวแปรอยู่ในบลอคไหนก็จะอยู่แค่ในบลอคนั้น ไม่เสนอหน้าดีดตัวเองออกไปใกล้ฟังก์ชันแบบที่ var ทำ

// พิมพ์ 2 ออกมาทั้งสองครั้ง
for(var i = 0; i < 2; i++) {
  // เนื่องจากในจังหวะที่ console.log ทำงาน loop ได้วนไปจนครบแล้ว
  // ทำให้ในขณะนั้่นค่า i มีค่าเป็น 2
  // อย่าลืมว่า i ประกาศโดยใช้ var มันจึงผลักตัวเองออกจาก for
  // หรือพูดง่ายๆคือ i นั้นเป็นตัวแปรตัวเดียวทุกครั้งที่วนลูปก็เพิ่มค่าใส่ตัวแปรเดิม
  setTimeout(function() { console.log(i) }, 100)
}

// ผลลัพธ์ได้ 0 และ 1
// ใช้ let ประกาศตัวแปรทำให้ตัวแปรเป็น block-scoped
// พูดง่ายๆคือ i จะเกิดขึ้นทุกครั้งที่วนลูป
// และไม่ซ้ำกับ i ก่อนหน้า
// เนื่องจากเป็น block-scoped มันจึงมีช่วงชีวิตอยู่แค่ใน {}
for(let i = 0; i < 2; i++) {
  setTimeout(function() { console.log(i) }, 100)
}

ความต่างของ let และ const คือ let นั้นหลังประกาศตัวแปรแล้วสามารถเปลี่ยนค่าได้ แต่ const ใช้สำหรับประกาศค่าคงที่ ทำให้หลังประกาศแล้วไม่สามารถเปลี่ยนแปลงค่าได้ การใช้ const นั้นมีข้อพึงระวังลองดูตัวอย่างครับ

let a = 0
a = 1

const PI = 3.14
PI = 1 // "PI" is read-only เปลี่ยนค่าไม่ได้อีกแล้วนะ

// obj เก็บ address หรือที่อยู่ของ { a: 1 }
// เราเปลี่ยน address ไม่ได้ เช่น obj = {} แบบนี้คือเปลี่ยน memory address ทำไม่ได้
const obj = { a: 1 }
// แต่การเปลี่ยนค่าภายใน object ไม่ได้ทำให้ memory address เปลี่ยนไป
obj.a = 2
console.log(obj) // { a: 2 }

เพื่อนๆที่เคยใช้ Redux จะทราบว่าเรานิยมใช้ switch/case กันใน reducer

export default (state = initialState, action) => {
  switch(action.type) {
    case LOAD_PAGES_SUCCESS:
      // ลดการเรียกชื่อยาวๆด้วยการประกาศตัวแปรมารองรับ
      // มั่นใจ 100% ว่า pages นี้ไม่เปลี่ยนแปลงแน่นอนเลยใช้ const ซะเลย
      const pages = action.result.entities.pages
      return { ...state, loading: false, items: pages }
    default:
      return state
  }
}

การเขียนแบบข้างบนฟังดูดีนะครับ เราอาจจะใช้ pages ไปทำสารพัดอย่างก่อน return ออกไปข้างนอก เราเลยสร้างตัวแปรมารองรับมัน แต่ถ้ามี case เพิ่มมาอีกอันแบบนี้หละ?

export default (state = initialState, action) => {
  switch(action.type) {
    case LOAD_PAGES_SUCCESS:
      const pages = action.result.entities.pages
      return { ...state, loading: false, items: pages }
    case LOAD_PAGE_SUCCESS:
      // ประกาศแบบนี้พังทันที นั่นเป็นเพราะ const เป็น block-scoped
      // ทำให้ scope ของมันอยู่ภายใต้ {} ของ switch
      // ใน case ก่อนหน้าเราประกาศ pages ไปแล้วจึงชนกัน
      // ควรย้ายการประกาศตัวแปรที่ทำซ้อนแบบนี้ออกไปข้างนอก
      const pages = action.result.entities.pages
      return { ...state, loading: false, items: [pages[0]] }
    default:
      return state
  }
}

อย่าคิดว่าเรื่องแบบนี้ไม่เคยเกิดขึ้นนะครับ เพราะมันมีคำถามใน stackoverflow มาแล้ว

ES2015 Module

JavaScript ไม่เคยจัดการโมดูลได้ด้วยตัวเองมาก่อนต้องทำผ่านไลบรารี่อย่าง CommonJS หรือ AMD การมาของ ES2015 มาพร้อมกับการสนับสนุนการทำงานกับโมดูลในตัว ตอนนี้คุณสามารถใช้ ES2015 เพื่อ import/export ของจากไฟล์หนึ่งไปอีกไฟล์หนึ่งได้แล้ว ดังนี้

// dog.js
export const DEFAULT_COLOR = 'white'
export function walk() {
  console.log('Walking...')
}

// main.js
// เลือกนำเข้าเฉพาะ DEFAULT_COLOR
import { DEFAULT_COLOR } from './dog.js'

// main.js
// นำเข้าทุกสรรพสิ่งที่ export จาก dog
// แล้วตั้งชื่อใหม่ให้ว่า lib
import * as lib from './dog.js'

ถ้าหากโมดูลนั้นมีแค่สิ่งเดียวที่อยาก export ทำได้ดังนี้

// circle.js
// สังเกตคำว่า default
export default class Circle {
  area() {
    
  }
}

// main.js
import Circle from './circle.js'

ES2015 module นั้นฉลาด มันมีการตรวจสอบว่าเรา import อะไรเข้ามาบ้าง ถ้าสิ่งไหนไม่ได้ import มันจะไม่นำมารวม ทำให้ไฟล์มีขนาดเล็กกว่าการใช้ CommonJS module เนื่องจาก CommonJS จะ import ทุกสรรพสิ่งที่โมดูลนั้น export ออกมา

// dog.js
export const DEFAULT_COLOR = 'white'
export function walk() {
  console.log('Walking...')
}

// main.js
// เลือกนำเข้าเฉพาะ walk
import { walk } from './dog.js'
walk()

// ผลลัพธ์สุดท้ายจะเป็น...
// สังเกตว่าไม่มี DEFAULT_COLOR ติดมาด้วย
function walk() 
  console.log('Walking...')
}
walk()

เรื่องที่ต้องพึงระวังในการใช้ ES2015 module มีดังนี้

  • ให้ import เฉพาะสิ่งที่จำเป็นต้องใช้จริงๆ
import _ from 'lodash'

_.map([1, 2, 3], item => item * 2)

// จากตัวอย่างนี้เรา import ทุกสิ่งที่ lodash มีมาใส่ตัวแปร _
// ทั้งๆที่ความจริงเราใช้แค่ map
// วิธีนี้จะทำให้ไฟล์ผลลัพธ์ของเรามีขนาดใหญ่ เพราะอุดมไปด้วยของที่ไม่ใช้
// วิธีต่อไปนี้จึงเหมาะสมกว่า

import map from 'lodash/map'
map([1, 2, 3], item => item * 2)
  • นอกจากนี้เพื่อนๆควรตรวจสอบให้แน่ใจว่าไลบรารี่ที่เรานำมาใช้นั้นมีอะไรใน JavaScript ที่ใช้ทดแทนได้หรือไม่ เพื่อจะได้ไม่ทำให้ไฟล์ผลลัพธ์มีขนาดใหญ่จากการใช้ไลบรารี่เหล่านั้น เช่น จากตัวอย่างข้างบนพบว่าสามารถใช้ Array#map ใน JavaScript แทนได้โดยไม่ต้องพึ่ง lodash

  • ตรวจสอบให้แน่ใจว่าขั้นตอนการทำงานของ build tool ของคุณ แอบแปลงโค๊ดคุณเป็น CommonJS ก่อนหรือไม่ ถ้ามีการแปลงนั่นหมายความว่า แม้คุณจะ import บางสิ่งเข้ามา แต่ด้วยความเป็น CommonJS มันจะ import ทุกสรรพสิ่งแม้คุณไม่ต้องการ ตัวอย่างเช่นการใช้ import/export ใน Webpack1

// ถ้าเพื่อนๆใช้ Webpack1 แม้เราจะเลือกเฉพาะสามตัวนี้ให้ import เข้ามา
// แต่ด้วยความที่ Webpack1 แปลงเป็น CommonJS ก่อน
// import ที่เราทำจึงกลายเป็นการ import ทุกสิ่งเข้ามาในไฟล์อยู่ดี
// เพียงแต่มี map, filter และ reduce ที่เรียกใช้งานได้
import { map, filter, reduce } from 'lodash'

// ควรเปลี่ยนแปลงเป็นสิ่งนี้
import map from 'lodash/map'
import filter from 'lodash/filter'
import reduce from 'lodash/reduce'

สำหรับเพื่อนๆคนไหนที่เผลอเรียก import _ from ‘lodash’ ไปแล้วก็ไม่ต้องเสียใจ ผมมี babel plugin ตัวนึงมาฝาก ที่จะทำแปลงการ import ของเพื่อนๆให้เป็นตามที่ผมแนะนำ รออะไรอยู่เล่า โหลดเลยซิ

ที่กล่าวไปทั้งหมดในหัวข้อนี้เป็นการอิมพอร์ตแบบ static แต่ถ้าเราต้องการอิมพอร์ตแบบ dynamic หรืออิมพอร์ตในช่วย runtime หละ? โดยปกติเรามักใช้ require ในการอิมพอร์ตกันใช่ไหมครับ แต่มันมีข้อเสียอยู่คือถ้าอิมพอร์ตไม่สำเร็จเราก็ไม่สามารถจัดการกับข้อผิดพลาดที่เกิดขึ้นได้ ใน ES2015 มีของเล่นใหม่ดังนี้

System.import('module_name')
  .then(module => { ... })
  // จัดการ error ในนี้
  .catch(error => ...)

Destructuring

Destructuring เป็นฟีเจอร์สำหรับการดึงส่วนของข้อมูลออกมาทำให้เราเขียนโค๊ดได้ง่ายขึ้น

let person = { 
  age: 24, 
  gender: 'male', 
  name: { 
    firstName: 'firstName', 
    lastName: 'lastName'
  } 
}

// ถ้าเราต้องการค่าเหล่านี้ออกจากอ็อบเจ็กต์ ต้องมาประกาศตัวแปรแบบนี้
let age = person.age 
let genger = person.gender
let name = person.name
let firstName = name.firstName
let lastName = name.lastName

// หากใช้ Destructuring จะเหลือแค่นี้
let { age, gender, name } = person
let { firstName, lastName } = name

// แต่ในความเป็นจริงแล้ว name เป็นเพียงแค่ทางผ่าน
// เราไม่ต้องการมัน เราต้องการแค่ firstName และ lastName
// จึงใช้ Destructuring ซ้อนเข้าไปอีกครั้ง
// เพียงเท่านี้ตัวแปร name ก็จะไม่เกิดขึ้นมาให้รำคาญใจ
let { age, gender, name: { firstName, lastName } } = person

รู้จักกับ Spread Operator

Spread Operator หรือผมขอเรียกมันง่ายๆว่าเครื่องหมายแตกตัวแล้วกัน เป็นจุดไข่ปลาสามจุด (…) ที่เอาไปวางหน้าอาร์เรย์หรืออ็อบเจ็กต์แล้วมีผลทำให้เครื่องหมายที่ครอบมันอยู่หลุดออก ดูตัวอย่างกันเลย

let obj1 = { a: 1, b: 2 }
let obj2 = { c: 3, d: 4 }
console.log({ ...obj1, ...obj2 }) // {"a":1,"b":2,"c":3,"d":4}

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

สารพัดวิธีแบบใหม่ในการจัดการกับฟังก์ชัน

ES2015 มาพร้อมกับความสามารถในการจัดการฟังก์ชันที่มากขึ้นดังนี้

Arrow Function

จากเดิมที่เราต้องประกาศฟังก์ชันด้วยการใช้คีย์เวิร์ด function ใน ES2015 เราลดรูปการประกาศให้เหลือเพียงลูกศรสองเส้นหรือ fat arrow เหมือนในภาษา CoffeeScript ดังนี้

// ES5
function(arguments) {

}

// ES2015
(arguments) => {

}

Arrow function ไม่ได้ต่างเพียงแค่ไวยากรณ์ที่เปลี่ยนไป แต่ arrow function นั้นยังเข้าถึง this จาก scope ที่ครอบมันอยู่ (Lexical binding) ดังนี้

function Dog() {
  this.color = 'white'
  
  setTimeout(function() { 
    // this ตัวนี้หมายถึง this ใน context ของฟังก์ชันนี้
    // จึงไม่มีการพิมพ์อะไรออกไป เพราะในฟังก์ชันนี้ this ไม่มีค่าของ color
    console.log(this.color) 
  }, 100)
}

new Dog()

// ถ้าต้องการให้พิมพ์ค่า color ออกมาต้องแก้ไขใหม่เป็น
function Dog() {
  this.color = 'white'
  let self = this
  
  setTimeout(function() { 
    // เรียกผ่านตัวแปร self แทน
    console.log(self.color) 
  }, 100)
}

// หรือใช้ arrow function ดังนี้
function Dog() {
  this.color = 'white'
  
  setTimeout(() => { 
    // this ของ arrow function นี้จะหมายถึง
    // this ตัวบน
    console.log(this.color) 
  }, 100)
}

กรณีของ Arrow Function ถ้าตัว body ของฟังก์ชันไม่ครอบด้วย {} และมี statement เดียว จะถือว่าค่านั้นคือค่าที่ return ออกจากฟังก์ชัน

const fn = () => 3

console.log(fn()) // 3

นอกจากนี้เราสามารถละ () ได้ถ้าพารามิเตอร์นั้นมีเพียงตัวเดียว

const arr = [1, 2, 3]
// มีค่าเท่ากับ arr.map((x) => x * x)
arr.map(x => x * x)

Default Values

ใน ES5 เราตรวจสอบค่าของพารามิเตอร์ที่ส่งเข้ามาในฟังก์ชัน หากไม่มีการส่งค่าเข้ามาเราอาจตั้งค่าเริ่มต้นของตัวแปรไว้ภายในฟังก์ชัน สำหรับ ES2015 เราสามารถกำหนดค่าเริ่มต้นของพารามิเตอร์ได้เลยด้วยการประกาศไว้ในส่วนประกาศฟังก์ชัน ดังนี้

// ES5
function foo(genger) {
  gender = gender || 'male;
}

// ES2015
function foo(gender = 'male') {

}

Named Parameters

ใน ES5 บางครั้งเราส่ง object เข้าไปในฐานะของ options พร้อมทั้งมีการตรวจสอบค่าต่างๆในอ็อบเจ็กต์ ถ้าค่าไหนไม่มีก็จะกำหนดค่าเริ่มต้นให้ ใน ES2015 เราสามารถใช้ destructuring เพื่อทำสิ่งเดียวกันกับที่ทำใน ES5 ได้ดังนี้

// ES5
function request(options) {
  var method = options.method || 'GET'
  var ssl = options.ssl || false
  console.log(method)
}
request({})

// ES2015
function request({ method='GET', ssl=false }) {
  console.log(method)
}
request({})

นอกจากนี้เราอาจต้องการตรวจสอบค่าที่อ็อบเจ็กต์ที่ส่งเข้ามาก่อนด้วยว่ามีค่าหรือไม่ ถ้าไม่มีให้กำหนดค่าเริ่มต้นเป็น {} ดังนี้

// ES5
function request(options) {
  options = options || {}
  var method = options.method || 'GET'
  var ssl = options.ssl || false
  console.log(method)
}
request()

// ES2015
// กำหนดค่าเริ่มต้นเป็น {}
function request({ method='GET', ssl=false } = {}) {
  console.log(method)
}

Rest Parameters

ไม่ใช่พารามิเตอร์ทั้งหมดที่เราสนใจตั้งชื่อให้ ลองดูตัวอย่างต่อไปนี้ครับ เราจะทำการบวกเลขกันโดยผมต้องการรับพารามิเตอร์แค่สองค่า ค่าแรกเป็นค่าเริ่มต้นยัดใส่ตัวแปรชื่อ initial ส่วนที่เหลือจะมีกี่ตัวเลขไม่สน ยัดใส่ตัวแปรชื่อ rest ดังนี้

// ไม่ว่าจะส่งตัวเลขเข้ามากี่ตัว ตัวแรกจะเป็น initial 
// ส่วนตัวอื่นจะเก็บอยู่ในอาร์เรย์ชื่อ rest
function sum(initial, ...rest) {
  return rest.reduce((prev, cur) => prev + cur, initial)
}

console.log(sum(10, 1, 2, 3)) // 16

ลดความซ้ำซ้อนด้วยการเขียนให้สั้นลง

ใน ES2015 นั้นเพื่อนๆสามารถลดรูปการประกาศฟังก์ชันในอ็อบเจ็กต์ โดยละคีย์เวิร์ด function ดังนี้

// ES5
const obj = {
  foo: function() {
  
  }
}

// ES2015
const obj = {
  foo() {
  
  }
}

นอกจากนี้ถ้า key ของอ็อบเจ็กต์มีชื่อตรงกับตัวแปลที่จะใส่เข้าไปเป็น value แล้ว เพื่อนๆสามารถละรูปได้เช่นกันดังนี้

// ES5
const foo = 'foo'
const bar = 'bar'

const obj = {
  foo: foo,
  bar: bar
}

// ES2015
const foo = 'foo'
const bar = 'bar'

const obj = {
  foo,
  bar
}

Template String

เมื่อก่อนเราใช้ + เพื่อต่อข้อความ แต่สำหรับ ES2015 นั้น Template String ทำให้การต่อข้อความเป็นเรื่องง่ายขึ้น ใช้ `` ครอบข้อความที่จะทำเป็น template string จากนั้นใช้ ${} สำหรับส่วนที่ต้องการแทรกส่วนของ JavaScript

const name = 'Nuttavut Thongjor'
console.log(`สวัสดีชาวโลก ผมชื่อ${name}`)

// นอกจากนี้ template string ยังเอื้อต่อการทำ multiline string ด้วย
const longString = `
  Lorem Ipsum is simply dummy text of the printing 
  and typesetting industry. 
  Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, 
  when an unknown printer took a galley of type and scrambled 
  it to make a type specimen book. 
`

การใช้งานคลาสใน ES2015

เป็นที่ทราบกันดีว่า JavaScript นั้นเป็นภาษาแบบ prototype-based เราจำลองฟังก์ชันให้เป็นคลาสและสร้างเมธอดผ่าน prototype นั่นเป็นสิ่งที่เราทำมากันเสมอ ใน ES2015 ได้นำการสร้างคลาสเข้ามาสู่ไวยากรณ์ทางภาษา แต่นั่นไม่ได้หมายความว่า prototype-based แบบเดิมๆจะหายไป

// ประกาศคลาสผ่านคีย์เวิร์ด class
class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  
  static species = 'Homo sapiens sapiens'
  
  walk() {
    console.log("I'm walking...")
  }
  
  print() {
    console.log(`My name is ${this.name}`)
    console.log(`I'm ${this.age}'`)
  }
}

const person = new Person('MyName', 99)
person.walk() // I'm walking...
person.print() // My name is MyName \n I'm 99'

// static method เรียกตรงผ่านคลาสได้เลย
console.log(Person.species) // Homo sapiens sapiens

// สำหรับการทำ inheritance สามารถใช้คีย์เวิร์ด extends ดังนี้
class Female extends Person {

}

Promise

เพื่อไม่ให้เป็นการเขียนบทความทับซ้อน เชิญเพื่อนๆอ่านบทความที่ผมเคยเขียนแล้วในเรื่องกำจัด Callback Hell ด้วย Promise และ Async/Await

Lodash และ ES2015

ผมค่อนข้างมันใจเลยทีเดียวว่าเพื่อนๆในที่นี้รู้จักและใช้ Lodash กันอยู่ ข่าวดีคือการมาของ ES2015 ทำให้คุณอาจไม่จำเป็นต้องใช้ Lodash อีกต่อไป เนื่องจากมีหลายๆฟังก์ชันที่สามารถทดแทน Lodash ได้แล้ว เช่น

[1, 2, 3].includes(1)
'Hello World'.startsWith('Hello')

บทความนี้ต้องการนำเสนอ ES2015 ที่น้อยที่สุดที่ควรรู้ ความจริงแล้ว ES2015 เป็นเรื่องค่อนข้างใหญ่ มีหลายหัวข้อที่เปิดมิติการเรียนรู้ใหม่ๆในโลก JavaScript เพื่อนๆที่สนใจสามารถอ่านเพิ่มเติมได้ครับ


แสดงความคิดเห็นของคุณ


ข้อความตอบกลับ
Nuttavut Thongjor6 เดือนที่ผ่านมา

ขอบคุณมากๆครับสำหรับข้อมูล 😃

จริงอย่างที่ว่าครับ ASI มีมาก่อน ES6 ออก แต่ สมัย ES5 แม้เราจะละ semicolon ออกได้ แต่เราก็จะไม่ทำกันนั่นเพราะเครื่องมือหลายตัวจะทำเอาเจ๊งถ้าเอา semicolon ออก เช่นพวก compressors

การมาของ ES6 มาพร้อมกับเครื่องมือที่ทำให้ความหนักใจของการเอา semicolon ออกลดลง จนเราเริ่มนิยมเอา semicolon ออกสำหรับโค้ด ES6 และยังคง semicolon ไว้สำหรับโค้ด ES5 ครับ

อ้างอิง: https://github.com/rse/es6-features

ขอบคุณสำหรับข้อมูลอีกครั้ง ผมได้ทำการเพิ่มข้อมูลเพิ่มเติมในบทความแล้วครับ 😃


Watcharamet Srinethirothai6 เดือนที่ผ่านมา

ขอบคุณครับ บทความของคุณ ทำให้โลกของผมง่ายขึ้น


Nuttavut Thongjorปีที่แล้ว

😃


ไม่ระบุตัวตนปีที่แล้ว

กราบงามๆหนึ่งครั้ง ขอบคุณครับ