JS-105 รู้หรือไม่ Import/Require มันต่างกันกว่าที่คิด !!

noomerZx
2 min readNov 13, 2019

--

credit: https://www.makiplace.com/blog/a-guide-to-javascript-filter-function/

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

CommonJS Modules (using “Require”)

CommonJS คือรูปแบบการเขียน JavaScript แบบแบ่ง Code ออกเป็นสัดเป็นส่วนหรือเป็น Unit ย่อยๆ เพื่อให้ง่ายต่อการพัฒนาโปรเจคขนาดใหญ่ขึ้น จุดแข็งคือการทำงานแบบ Synchronous

ตัวอย่าง

// somefile.js
exports.log = function (text) {
console.log(text)
}
// anotherfile.js
const logFn = require('./somefile.js')
// using
logFn('Hello World')

ES6 Modules (using “Import”)

ในสมัยก่อนนั้น JavaScript ยังไม่สามารถจัดการเรื่องของ Modules ได้ด้วยตัวเอง ทำให้ต้องใช้ Libs ต่างๆเข้ามาช่วยจัดการ เช่น CommonJS/AMD เป็นต้น ซึ่งการมาของ ES2015/ES6 นั้นได้มีการอัพเดทฟังก์ชั่น ES6 Modules เข้ามาทำให้ตัว JavaScript สามารถจัดการการเขียนแบบ Modules ได้ด้วยตัวเองแล้ว

ตัวอย่าง

// somefile.js
export const text = 'Hello World'
// anotherfile.js
import { text } from 'somefile.js'
console.log(text)

CommonJS vs ES6 Modules

อธิบายพื้นฐานกันไปแล้ว จริงๆหลายๆคนน่าจะรู้จักและเคยใช้กันมาบ้าง เรามาว่าถึงความแตกต่างและข้อควรระวังกันดีกว่า ผมคิดว่าหลายๆท่านอาจจะไม่เคยทราบมาก่อนก็ได้นะ มาลองดูตัวอย่างกัน

ลองเขียน JS แบบ CommonJS กันก่อน

// lib.js
var number = 0
function increaseNumber() {
number++
}
module.exports = {
number: number,
increaseNumber: increaseNumber
}

ทีนี้มาลองเขียน Code ที่ทำงานเหมือนกันในแบบ ES6 Modules บ้าง

// lib.js
export let number = 0
export function increaseNumber() {
number++
}

เพื่อนๆคิดว่า การเขียน Code ด้วย 2 วิธีข้างต้นนั้น ให้ผลลัพธ์แตกต่างกันหรือไม่ ??

ผมให้เวลาคิดสัก 2–3 นาที… ติ๊ก ต่อก .. ติ๊กกก …. ต่อก ..

….

..

…….. มาลองไปด้วยกันทั้ง 2 วิธีกันโลดดด

CommonJS Example Usage

มาลองใช้งานแบบ CommonJS กันก่อน

var libs = require('./lib')console.log(libs.number) // = 0
libs.increaseNumber()
console.log(libs.number) // = 0

จากตัวอย่างข้างต้น จะเห็นว่าค่า number นั้นไม่ได้ถูกเพิ่มค่าขึ้นด้วยฟังก์ชัน increaseNumber ! แต่ถ้าเราอยากให้มันเพิ่มล่ะต้องเขียนยังไง ?

var libs = require('./lib')console.log(libs.number) // = 0
libs.number++
console.log(libs.number) // = 1

การ Import ของ CommonJS นั้นจะเป็นการ Copy Object ออกมาใช้งาน ทำให้ค่า number นั้นถูก copy ออกมาด้วย ดังนั้นการเรียกใช้งาน function increaseNumber ไม่ส่งผลต่อค่า number ที่ถูก copy ออกมาแล้วนั่นเอง แต่อย่างไรก็ตาม libs.number ที่ถูก Copy ออกมานั้นยังคงเปลี่ยนแปลงค่าได้เสมอ ทำให้การบวกค่าเข้าไปตรงๆด้วยคำสั่ง libs.number++ ทำให้ค่าเปลี่ยนไป

ES6 Modules Example Usage

ในทางกลับกัน ES6 Modules นั้นทำงานต่างจาก CommonJS โดยสิ้นเชิง แต่ก่อนอื่นมาดูวิธีเรียกใช้กันก่อน

import { number, increaseNumber } from './lib'

console.log(number) // 0
increaseNumber()
console.log(number) // 1

จะเห็นว่าผลลัพธ์การเรียกใช้ของทั้ง 2 วิธีนั้นแตกต่างกันเพราะ ES6 Modules นั้นทำงานแบบ import bindings ดังนั้นค่าตัวแปรจึง references ถึงกันเสมอ ทำให้มีความอันตรายอยู่ หากใช้อย่างไม่ระวังหรือไม่เข้าใจมันดีพอ

จริงๆการ Bindings ของ ES6 Modules นั้นทำมาแก้ปัญหา “Cyclic Dependencies” ของ CommonJS แต่ก็ไม่ได้แก้ปัญหาได้ 100% เสียทีเดียว หากสนใจก็ไปอ่านเพิ่มเติมได้ทื่ References เลยครับ เพราะมันค่อนข้างละเอียดพอสมควร

สรุป

ไม่ว่าจะเขียนด้วยวิธีไหนสิ่งที่ต้องคำนึงถึงมากที่สุดคือการเข้าใจในสิ่งที่เราเขียน เพราะจะทำให้ระบบนั้นมี Bugs น้อยที่สุดเท่าที่จะเป็นไปได้ หรือถ้าเกิดมี Bugs ขึ้นมาเราก็จะรู้ว่าเกิดขึ้นเพราะอะไรและสามารถแก้ไขได้ทันท่วงทีครับผม

สุดท้ายก็มีข้อควรระวังนิดหน่อยสำหรับคนที่อยากจะไปลองเล่นดู คือการใช้งานบน Browser อาจจะต้อง setup Babel/Webpack มาช่วย หรือถ้าอยากลองเล่นด้วย Node.js ตัว ES6 Modules นั้นจะต้องใช้ Node version 12+ และต้องเขียนเป็น .mjs หรือระบุ type module ใน package.json ด้วย และต้องใส่ option — experimental-modules เพิ่มตอนจะ run อีก โอย… ยุ่งยากเป็นบ้า

แต่…. ถ้าใครขี้เกียจก็เข้าไปดู Code ตัวอย่างของผมผมได้ ที่นี่

--

--

noomerZx
noomerZx

Written by noomerZx

Software Engineer, Blogger, Runner

No responses yet