φυβλαςのβλογ
บล็อกของ phyblas



javascript เบื้องต้น บทที่ ๓๖: การสร้างและใช้งานมอดูล
เขียนเมื่อ 2020/02/04 13:02
แก้ไขล่าสุด 2020/03/19 14:07


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

ใน ES6 มีวิธีที่จะนำโค้ดจากไฟล์อื่นเข้ามาใช้ในอีกไฟล์หนึ่งได้ นั่นคือวิธีการทำเป็นมอดูล (module) ซึ่งจะเขียนถึงในบทนี้




การเตรียมการ

ในบทนี้จะต่างจากบทอื่นที่ผ่านมาตรงที่ไม่สามารถรันผ่านไฟล์ในเครื่องได้

ในบทที่ ๒ ได้แนะนำวิธีการรันโค้ดในเบราเซอร์แบบง่ายๆในเบื้องต้นเพื่อใช้ในการฝึก

แต่วิธีการง่ายๆนี้จะมีปัญหาเวลาที่จะใช้งานมอดูล เนื่องจากปัญหาในเรื่องของ CORS (Cross-Origin Resource Sharing) ซึ่งเป็นเรื่องของความปลอดภัยในการใช้เบราเซอร์

ปกติเบราเซอร์จะมีการป้องกันไว้ไม่ให้ใช้โค้ดที่มาจากแหล่งที่ไม่ได้ขึ้นต้นด้วย http รวมถึงไฟล์ที่อยู่ในเครื่องเองก็ไม่ได้

วิธีการที่แนะนำไปนั้นคือสร้างไฟล์ .html และ .js ในเครื่องโดยตรง จึงไม่สามารถใช้งานมอดูลได้ แม้ว่าโค้ดทั้งหมดจะอยู่ในเครื่องเดียวกันก็ตาม

เพื่อที่จะใช้งานมอดูลได้ จะต้องเอาไฟล์ไปใส่ในเว็บแล้วเปิดเว็บขึ้นมา หรือไม่ก็เปิดเซอร์เวอร์ขึ้นมาในเครื่อง แล้วเปิดแบบ local (เช่นผ่าน http://0.0.0.0:3000) ก็ได้

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

หากใครที่ไล่อ่านมาเรื่อยๆโดยใช้รันไฟล์ในเครื่องผ่านเบราเซอร์โดยตรง และไม่สะดวกที่จะเปลี่ยนไปวางในเว็บ ก็อาจข้ามบทนี้ไป แต่จะไม่กระทบต่อเนื้อหาในบทอื่นๆ

ในที่นี้เพื่อการฝึกใช้มอดูลในที่นี้จะต้องใช้ ๓ ไฟล์ ขอตั้งชื่อตามนี้ (จะเปลี่ยนเป็นชื่ออะไรก็ได้ แต่ต้องเปลี่ยนโค้ดในส่วนที่มีชื่อไฟล์ให้ตรงกัน)
  • mocha.html ไฟล์ .html หน้าเว็บ
  • maccha.js ไฟล์ .js ที่เขียนโค้ดจาวาสคริปต์ตัวหลักที่จะถูกเรียกไปลงในรันในหน้าเว็บ
  • momo.js ไฟล์ .js ที่ใส่โค้ดมอดูลที่จะถูกเรียกไปใช้ใน .js อีกไฟล์

ทั้งหมด ๓​ ไฟล์นี้วางไว้ที่เดียวกันทั้งหมด

momo.js
export function sawatdi(){
  alert("สวัสดีชาวโลก! 你好,世界");
}

maccha.js
import {sawatdi} from './momo.js';
sawatdi();

mocha.html
<meta charset="UTF-8" />
<script src="./maccha.js" type="module"></script>

จากนั้นเปิดหน้า mocha.html ขึ้นมาก็จะเด้งขึ้นมาแบบนี้


ชื่อไฟล์จำเป็นต้องมี ./ นำหน้า ถ้าอยู่ในโฟลเดอร์เดียวกัน จะละไม่ได้

ในที่นี้ฟังก์ชัน sawatdi ซึ่งถูกเขียนในไฟล์ momo.js จะถูกนำไปใช้ได้โดยคำสั่ง export ส่วนในไฟล์ maccha.js ให้ใช้คำสั่ง import เพื่อดึงฟังก์ชัน sawatdi มาใช้

สุดท้าย mocha.html เขียนเรียกโค้ดของ maccha.js ผ่านแท็ก script โดยในที่นี้ในแท็กจำเป็นต้องใส่ type="module" ด้วย ไม่เช่นนั้นจะเกิดข้อผิดพลาด

ข้อความอธิบายข้อผิดพลาดที่ขึ้นมาอาจต่างกันตามเบราเซอร์ที่ใช้ แต่ในความโดยรวมก็คือไม่สามารถใช้คำสั่ง import ได้ ถ้าไม่ทำให้ไฟล์หลัก (ไฟล์ที่ถูกเรียกจาก html โดยตรง ในที่นี้คือ maccha.js) เป็นมอดูลด้วย ซึ่งการใส่ type="module" ก็คือเพื่อกำหนดชนิดให้โค้ดในไฟล์นั้นเป็นมอดูล

หากว่าใส่ type="module" แล้วเจอข้อผิดพลาด โดยชี้ปัญหาที่เกี่ยวกับ CORS แบบนั้นอาจหมายความว่ากำลังรันผ่านไฟล์ในเครื่อง ไม่ได้เอาลงเว็บดังที่กล่าวข้างต้น ทำให้โดนเบราเซอร์ป้องกัน




การ export และ import มอดูล

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

การ export มอดูลมี ๒ แบบ คือแบบ export เฉยๆธรรมดากับ export default ในที่นี้จะเริ่มจาก export ธรรมดาก่อน

ในส่วนของไฟล์มอดูลจะใช้คำสั่ง export นำหน้าฟังก์ชันหรือตัวแปรต่างๆที่ต้องการเอาไปใช้
// ประกาศและ export ค่าคงที่
export const planck = 6.62607004e-34;
// ประกาศและ export ฟังก์ชัน
export function thakthai(){
  alert("คนนิจิวะ");
};
// ประกาศฟังก์ชันและค่าก่อนแล้วค่อย export
let matane = "มาตะเน้";
function bokla(){
  alert(matane+"~~");
};
export {bokla, matane};

จากตัวอย่างจะเห็นว่าประกาศค่าหรือฟังก์ชันแล้ว export เลย หรือว่าสร้างตัวแปรแล้วค่อยเอามา export อีกทีก็ได้ ในกรณีนี้แค่ใส่ชื่อตัวแปรทั้งหมดที่ต้องการไว้ใน { }

จากนั้นฝั่งไฟล์ที่จะเรียกใช้มอดูลก็สั่ง import {ชื่อตัวแปร, ...} from ชื่อไฟล์มอดูล
import {planck, matane, thakthai, bokla} from './momo.js';
alert(planck); // ได้ 6.62607004e-34
thakthai(); // ได้ คนนิจิวะ
alert(matane); // ได้ มาตะเน้
bokla(); // ได้ มาตะเน้~~

หรือถ้าต้องการจะเรียกทั้งหมดก็เขียนในรูป import * as ชื่อที่ต้องการแทนมอดูล from ชื่อไฟล์มอดูล แล้วเวลาจะใช้ก็ต้องใส่ชื่อที่แทนมอดูลนั้นลงไปนำหน้าแล้วค่อยตามด้วยชื่อตัวแปรหลังจุด
import * as momi from './momo.js';
alert(momi.planck);
momi.thakthai();
alert(momi.matane);
momi.bokla();

นอกจากนี้จะนำเข้ามาโดยตั้งชื่อใหม่ให้ก็ได้ โดยใส่ as ให้ตัวที่ต้องการเปลี่ยนแล้วตามด้วยชื่อที่ต้องการ ส่วนตัวไหนไม่ได้ต้องการเปลี่ยนชื่อก็ไม่ต้องใส่ as ก็ได้
import {planck as h, thakthai as tt, matane as mtn, bokla} from './momo.js';
alert(h); // ได้ 6.62607004
tt(); // ได้ คนนิจิวะ
alert(mtn); // ได้ มาตะเน้~
bokla(); // ได้ มาตะเน้~




การใช้ export default

นอกจากวิธีการ export ธรรมดาแล้ว อีกวิธีการคือใช้ export default คือการรวมข้อมูลทั้งหมดอยู่ในออบเจ็กต์เดียวแล้ว export ออกไป

ในไฟล์มอดูล ให้ใช้คำสั่ง export default ตามด้วยออบเจ็กต์ที่ต้องการเอาไปใช้
let c = 299792458;
const PI = 3.14159;
export default { c, PI };

ส่วนในไฟล์ที่เรียกใช้มอดูลก็ให้ import แบบนี้
import momi from "./momo.js"
alert(momi) // ได้ [object Object]
alert(momi.c) // ได้ 299792458
alert(momi.PI) // ได้ 3.14159

ในที่นี้ momi คือชื่อออบเจ็กต์ที่จะมาใช้แทนออบเจ็กต์มอดูลนั้น จะตั้งเป็นอะไรก็ได้ ให้ระวังว่าต่างจากการใช้กับมอดูลที่ export ธรรมดาตรงที่ไม่ต้องใช้ {} คร่อม

export default นั้นสามารถทำได้แค่ตัวเดียว แต่ว่าโดยทั่วไปสิ่งที่ export default จะเป็นออบเจ็กต์ที่ใส่ค่าอะไรต่างๆอยู่ข้างใน ดังนั้นผลที่ได้ก็ไม่ต่างจากการ export ตัวแปรทีละตัว แต่ต้องระวังว่าการ import จะต่างกัน

ที่จริงสิ่งที่ใช้ export default จะไม่ใช่ออบเจ็กต์แต่เป็นอย่างอื่นก็ได้ แม้จะดูไม่ค่อยมีความหมายที่จะทำอย่างนั้นนัก

เช่น เขียน export default ค่าตัวเลขแบบนี้ในมอดูล
export default 1000;

แล้วในไฟล์ที่เรียกใช้ก็เอาค่ามาใช้แบบนี้
import momi from "./momo.js"
alert(momi) // ได้ 1000




การใช้ export ทั้ง ๒​ แบบไปพร้อมกัน

ทั้ง export ธรรมดาและ export default สามารถใช้ได้ในเวลาเดียวกัน เช่นทำแบบนี้
const R = 8.31446262;
let u = 1.66053907e-27;
export {R, u};

export default {
  e: 1.60217663e-19,
  G: 6.6743e-11
};

แล้วในส่วนของไฟล์เรียกใช้ก็เขียนแบบนี้
import momi, { R, u } from "./momo.js"
alert(momi.e) // ได้ 1.60217663e-19
alert(momi.G) // ได้ 6.6743e-11
alert(R) // ได้ 8.31446262
alert(u) // ได้ 1.66053907e-27

จะเห็นว่าต้องแยก import เป็น ๒ ส่วน ส่วน momi นี่คือแทนส่วนที่ export default ส่วน { R, u } แทนส่วนของ R และ u ที่ export แบบธรรมดามา

ส่วนที่ export ธรรมดาจะเปลี่ยนมาใช้ * as แบบนี้ก็ได้เช่นกัน
import momi, * as momo from "./momo.js"
alert(momi.e)
alert(momi.G)
alert(momo.R)
alert(momo.u)




การใช้ซิมโบลเพื่อสร้างพรอเพอร์ตีส่วนตัวในมอดูล

ในบทที่ ๒๕ ได้กล่าวถึงเรื่องพรอเพอร์ตีส่วนตัว (private property) ซึ่งหมายถึงพรอเพอร์ตีที่ต้องเข้าผ่านเมธอดของออบเจ็กต์เท่านั้น ไม่สามารถเข้าถึงได้โดยตรง

การใช้โคลเชอร์เป็นวิธีหนึ่งที่ใช้สร้างพรอเพอร์ตีส่วนตัวขึ้นได้ดังที่แนะนำไปในบทที่ ๒๕ อย่างไรก็ตาม ในกรณีที่ทำเป็นมอดูล มีอีกวิธีหนึ่งที่สามารถสร้างพรอเพอร์ตีส่วนตัว นั่นคือการใช้ข้อมูลชนิดซิมโบลช่วย

โดยอาศัยคุณสมบัติของซิมโบลซึ่งได้เขียนถึงไปในบทที่ ๓๕ สามารถใช้สร้างพรอเพอร์ตีส่วนตัวได้เช่นกัน

ตัวอย่างการใช้
let NAK = Symbol();
let SUNG = Symbol();
class Pokemon {  
  constructor(chue,nak, sung){
    this.chue = chue;
    this[NAK] = nak;
    this[SUNG] = sung;
  }
  bokKhomun(){
    alert(this.chue +" หนัก "+this[NAK] + " กก. สูง "+this[SUNG], "ม.");
  }
}

export {Pokemon}

จะเห็นว่า NAK และ SUNG เป็นซิมโบลที่ประกาศในไฟล์มอดูล และถูกใช้เป็นชื่อพรอเพอร์ตีให้ออบเจ็กต์ที่สร้างขึ้นในคอนสตรักเตอร์

แต่ว่าที่ export ไปมีแค่ตัวคลาส Pokemon เท่านั้น NAK และ SUNG ไม่ได้ถูก export ไปด้วย ดังนั้นจึงไม่มีทางที่จะเอาตัวแปรซิมโบล NAK และ SUNG เพื่อมาเรียกใช้พรอเพอร์ตีนี้โดยตรงได้

ดังนั้นพรอเพอร์ตีที่ใช้ซิมโบลเป็นชื่อจะกลายเป็นพรอเพอร์ตีส่วนตัวไป ได้แค่เรียกใช้ผ่านเมธอด bokKhomun เท่านั้น

ในไฟล์ที่เรียกใช้มอดูลก็เขียนแบบนี้เพื่อนำคลาส Pokemon มาใช้ แล้วก็ใช้เมธอดเพื่อดูข้อมูลได้ รวมทั้งพรอเพอร์ตี .chue ซึ่งไม่ได้ใช้ซิมโบลก็สามารถดูค่าได้โดยตรง
import {Pokemon} from "./momo.js";
let kamonegi = new Pokemon("คาโมเนงิ", 15, 0.8);
kamonegi.bokKhomun(); // ได้ คาโมเนงิ หนัก 15 กก. สูง 0.8
alert(kamonegi.chue); // ได้ คาโมเนงิ



เนื่องจากจาวาสคริปต์ไม่มีวิธีสร้างพรอเพอร์ตีส่วนตัวโดยตรง จึงต้องอาศัยวิธีโดยอ้อมแทนเช่นนี้




การใช้ซิมโบลเพื่อสร้างเมธอดส่วนตัวในมอดูล

ทำนองเดียวกันกับพรอเพอร์ตีส่วนตัว เมธอดส่วนตัว (private method) ก็สามารถสร้างได้เช่นกัน

เมธอดส่วนตัวคือเมธอดที่สามารถเรียกใช้ได้แค่จากเมธอดภายในออบเจ็กต์เท่านั้น ไม่สามารถเรียกใช้โดยตรงได้

เมธอดส่วนตัวสร้างโดยตอนที่นิยามเมธอดให้เขียนเป็น [ชื่อตัวแปรซิมโบล] แทนที่จะใช้ชื่อธรรมดา เช่น
const KHOMUN = Symbol();
class Digimon {
    constructor(chue, lv) {
        this.chue = chue;
        this.lv = lv;
    }
    // เมธอดส่วนตัว
    [KHOMUN]() {
        return this.chue + " lv " + this.lv;
    }
    // เมธอดที่จะเรียกใช้เมธอดส่วนตัว
    bokKhomun() {
        alert(this[KHOMUN]());
    }
}
export { Digimon };

จากนั้นก็นำมาใช้ดู
import {Digimon} from "./momo.js";
let agumon = new Digimon("อากุมอน",11);
agumon.bokKhomun(); // ได้ อากุมอน lv 11

ทั้งหมดนี้คือวิธีการสร้างและใช้งานมอดูลในจาวาสคริปต์





-----------------------------------------

囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧

ดูสถิติของหน้านี้

หมวดหมู่

-- คอมพิวเตอร์ >> เขียนโปรแกรม >> javascript

ไม่อนุญาตให้นำเนื้อหาของบทความไปลงที่อื่นโดยไม่ได้ขออนุญาตโดยเด็ดขาด หากต้องการนำบางส่วนไปลงสามารถทำได้โดยต้องไม่ใช่การก๊อปแปะแต่ให้เปลี่ยนคำพูดเป็นของตัวเอง หรือไม่ก็เขียนในลักษณะการยกข้อความอ้างอิง และไม่ว่ากรณีไหนก็ตาม ต้องให้เครดิตพร้อมใส่ลิงก์ของทุกบทความที่มีการใช้เนื้อหาเสมอ

สารบัญ

รวมคำแปลวลีเด็ดจากญี่ปุ่น
มอดูลต่างๆ
-- numpy
-- matplotlib

-- pandas
-- opencv
-- pytorch
การเรียนรู้ของเครื่อง
-- โครงข่าย
     ประสาทเทียม
maya
javascript
ความน่าจะเป็น
บันทึกในญี่ปุ่น
บันทึกในจีน
-- บันทึกในปักกิ่ง
-- บันทึกในฮ่องกง
-- บันทึกในมาเก๊า
บันทึกในไต้หวัน
บันทึกในยุโรปเหนือ
บันทึกในประเทศอื่นๆ
เรียนภาษาจีน
qiita
บทความอื่นๆ

บทความแบ่งตามหมวด



ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ

  ค้นหาบทความ

  บทความแนะนำ

ภาษาจีนแบ่งเป็นสำเนียงอะไรบ้าง มีความแตกต่างกันมากแค่ไหน
ทำความเข้าใจระบอบประชาธิปไตยจากประวัติศาสตร์ความเป็นมา
เรียนรู้วิธีการใช้ regular expression (regex)
หลักการเขียนทับศัพท์ภาษาจีนกวางตุ้ง
การใช้ unix shell เบื้องต้น ใน linux และ mac
หลักการเขียนทับศัพท์ภาษาจีนกลาง
g ในภาษาญี่ปุ่นออกเสียง "ก" หรือ "ง" กันแน่
ทำความรู้จักกับปัญญาประดิษฐ์และการเรียนรู้ของเครื่อง
ค้นพบระบบดาวเคราะห์ ๘ ดวง เบื้องหลังความสำเร็จคือปัญญาประดิษฐ์ (AI)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
บันทึกการเที่ยวสวีเดน 1-12 พ.ค. 2014
แนะนำองค์การวิจัยและพัฒนาการสำรวจอวกาศญี่ปุ่น (JAXA)
เล่าประสบการณ์ค่ายอบรมวิชาการทางดาราศาสตร์โดยโซวเคนได 10 - 16 พ.ย. 2013
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
บันทึกการเที่ยวญี่ปุ่นครั้งแรกในชีวิต - ทุกอย่างเริ่มต้นที่สนามบินนานาชาติคันไซ
หลักการเขียนทับศัพท์ภาษาญี่ปุ่น
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ
ทำไมถึงอยากมาเรียนต่อนอก
เหตุผลอะไรที่ต้องใช้ภาษาวิบัติ?

ไทย

日本語

中文