Masukkan Password
87 Different Import & Export Syntax
Lecture ini masih melanjutkan ES6 Modules, tetapi sekarang fokusnya lebih dalam: berbagai cara import dan export, terutama:
- Named Import
- Import semua dengan
* as - Alias dengan
as - Default Export
- Perbedaan default export vs named export
- Kenapa satu file module tidak dieksekusi berkali-kali walaupun di-import dari beberapa file
Kita bahas pelan-pelan.
1. Gambaran besar
Pada lecture sebelumnya kita belajar dasar:
// user.ts
export class User {}
Kemudian file lain:
// user-action.ts
import { User } from "../models/user.js";
Ini adalah named export + named import.
Sekarang dosen menunjukkan bahwa ES6 Modules punya beberapa variasi.
Secara umum:
EXPORT
│
├── Named Export
│ │
│ └── Named Import
│
├── Default Export
│ │
│ └── Default Import
│
└── Named Import dengan Alias
2. Named Import
Ini adalah cara yang sudah kita lihat sebelumnya.
Misalnya user-action.ts:
export function createUser() {
// ...
}
export function getUsers() {
// ...
}
Kemudian di app.ts:
import {
createUser,
getUsers
} from "./user-utils/user-action.js";
Ini disebut named import.
Kenapa disebut named?
Karena kita mengambil sesuatu berdasarkan nama yang diekspor.
File asal punya:
createUser
getUsers
dan kita meng-import dengan nama tersebut:
createUser
getUsers
Jadi:
user-action.js
┌─────────────────────┐
│ export createUser │
│ export getUsers │
└──────────┬──────────┘
│
│ named import
▼
app.js
┌─────────────────────┐
│ createUser │
│ getUsers │
└─────────────────────┘
3. Import semua menggunakan *
Dosen kemudian mengatakan:
Bagaimana kalau kita ingin mengambil semua yang di-export oleh suatu file?
Misalnya user-action.ts memiliki:
export function createUser() {
// ...
}
export function getUsers() {
// ...
}
Daripada:
import {
createUser,
getUsers
} from "./user-utils/user-action.js";
kita bisa menggunakan:
import * as actions
from "./user-utils/user-action.js";
Perhatikan:
*
dan:
as actions
4. Apa arti *?
* berarti:
Ambil semua named exports dari module tersebut.
Misalnya:
// user-action.ts
export function createUser() {}
export function getUsers() {}
Kemudian:
import * as actions
from "./user-utils/user-action.js";
Maka kira-kira kita mendapatkan:
actions
├── createUser
└── getUsers
Sehingga penggunaannya menjadi:
actions.createUser(...)
dan:
actions.getUsers()
5. Kenapa harus menggunakan actions.?
Karena kita mengatakan:
import * as actions
Artinya:
Ambil semua export dan masukkan ke dalam sebuah namespace/object bernama
actions.
Jadi daripada langsung:
createUser(...)
kita menggunakan:
actions.createUser(...)
dan:
actions.getUsers()
Secara konsep:
user-action.js
│
│ export
▼
┌────────────────────┐
│ createUser │
│ getUsers │
└─────────┬──────────┘
│
│ import * as
▼
actions
┌────────────────────┐
│ actions.createUser │
│ actions.getUsers │
└────────────────────┘
6. * bukan berarti benar-benar semua isi file
Ini poin yang ditekankan dosen.
Kalau user-action.ts:
const userList: User[] = [];
export function createUser() {}
export function getUsers() {}
Kemudian:
import * as actions
from "./user-utils/user-action.js";
Yang masuk ke actions hanya:
actions
├── createUser
└── getUsers
Bukan:
actions
├── createUser
├── getUsers
└── userList
Kenapa?
Karena:
const userList = [];
tidak di-export.
Jadi aturan sederhananya:
import *mengambil semua yang di-export, bukan semua variabel/function yang ada di dalam file.
7. Analogi import * as
Bayangkan sebuah toko punya barang:
Toko
├── Laptop ← dijual
├── Phone ← dijual
└── Kunci Gudang ← internal, tidak dijual
Kalau kamu mengatakan:
“Saya mau semua barang yang dijual.”
Maka kamu mendapatkan:
Laptop
Phone
Tapi tidak mendapatkan:
Kunci Gudang
Sama seperti:
import * as actions
yang mengambil semua:
export ...
tetapi tidak mengambil hal yang tidak di-export.
8. Named Import dengan alias
Sekarang kita masuk ke as dalam named import.
Misalnya:
export class User {}
Kita bisa melakukan:
import { User } from "../models/user.js";
Tetapi kita juga bisa memberi nama lain:
import { User as user } from "../models/user.js";
Ini disebut alias.
Artinya:
Import
User, tetapi di file ini saya ingin memanggilnya dengan namauser.
9. Setelah diberi alias, gunakan nama alias
Kalau kita menulis:
import { User as user }
from "../models/user.js";
maka jangan lagi menulis:
new User(...)
Gunakan:
new user(...)
Karena nama lokalnya sekarang:
user
Bukan:
User
Contoh:
import { User as user }
from "../models/user.js";
const person = new user("John", 28, "male");
10. Kenapa alias berguna?
Alias berguna ketika nama yang kita import:
- terlalu umum
- bentrok dengan nama lain
- ingin dibuat lebih jelas dalam konteks tertentu
Contoh:
import { User as AppUser } from "./user.js";
Kemudian:
const user = new AppUser(...);
Ini bisa membuat kode lebih jelas.
11. Sekarang masuk ke Default Export
Ini bagian yang sangat penting.
Sebelumnya kita punya:
export class User {}
Itu adalah named export.
Sekarang kita bisa mengatakan:
export default class User {}
Ini disebut default export.
12. Apa perbedaan named export dan default export?
Named export
export class User {}
Kita mengekspor sesuatu dengan nama:
User
Ketika import:
import { User } from "./user.js";
Nama harus cocok dengan export tersebut.
Default export
export default class User {}
Kita mengatakan:
“Ini adalah export utama/default dari module ini.”
Ketika import:
import User from "./user.js";
Perhatikan sesuatu:
tidak ada {}.
13. Named import menggunakan {}
Contoh:
export const pi = 3.14;
Import:
import { pi } from "./user.js";
Ada:
{ pi }
Karena pi adalah named export.
14. Default import tidak menggunakan {}
Misalnya:
export default class User {}
Import:
import User from "./user.js";
Tidak ada:
{ User }
Jadi:
Named export
↓
import { User }
Default export
↓
import User
Ini salah satu hal yang wajib kamu ingat.
15. Keistimewaan default export: nama bebas
Ini yang membedakan default export.
Misalnya:
// user.ts
export default class User {}
Kita bisa melakukan:
import User from "./user.js";
atau:
import Person from "./user.js";
atau bahkan:
import ABC from "./user.js";
Semua secara syntax bisa.
Kenapa?
Karena yang kita import adalah default export, bukan named export berdasarkan nama tertentu.
Misalnya:
import Person from "./user.js";
maka:
Person
↓
default export dari user.js
↓
User class
Jadi Person hanyalah nama lokal.
16. Analogi default export
Bayangkan sebuah file adalah sebuah kotak:
user.ts
┌───────────────────────┐
│ │
│ DEFAULT │
│ User │
│ │
│ NAMED │
│ pi │
│ │
└───────────────────────┘
Untuk mengambil default:
import Something from "./user.js";
Nama Something bebas.
Sedangkan named export:
import { pi } from "./user.js";
harus mengambil berdasarkan nama pi.
17. Satu module hanya punya satu default export
Dosen menekankan:
Satu file hanya boleh memiliki satu default export.
Misalnya ini valid:
export default class User {}
export const pi = 3.14;
export function hello() {}
Kita punya:
1 default export
+
2 named exports
Tetapi tidak boleh:
export default class User {}
export default class Product {}
Karena ada dua:
default export
default export
dalam satu module.
18. Satu file bisa punya banyak named exports
Ini boleh:
export const pi = 3.14;
export function hello() {}
export class User {}
export interface Person {}
Jadi:
Named exports
├── pi
├── hello
├── User
└── Person
Tidak ada masalah.
Tetapi default maksimal satu.
19. Default + Named Export sekaligus
Ini juga boleh.
Contoh:
export default class User {}
export const pi = 3.14;
Artinya file memiliki:
Default:
User
Named:
pi
Kemudian import:
import User, { pi } from "./user.js";
Perhatikan formatnya:
import User, { pi }
│ │
│ └── named import
│
└── default import
Ini pola yang sangat umum.
20. Bedakan import * as dengan default import
Ini sering membingungkan.
Default import
import User from "./user.js";
Mengambil default export.
Namespace import
import * as actions from "./user-action.js";
Mengambil semua named exports sebagai satu namespace/object bernama actions.
Jadi:
import User
berbeda dengan:
import * as actions
21. Contoh lengkap
Misalnya user.ts:
export default class User {
constructor(
public name: string,
public age: number
) {}
}
export const pi = 3.14;
File ini memiliki:
DEFAULT
└── User
NAMED
└── pi
Kemudian kita bisa:
import User, { pi } from "./user.js";
Lalu:
const user = new User("John", 28);
console.log(pi);
22. Sekarang bagian yang sangat menarik: module hanya dieksekusi sekali
Dosen kemudian membahas masalah lain.
Misalnya:
app.ts
│
├── import user.js
│
└── import user-action.js
│
└── import user.js
Jadi user.js di-import oleh dua file:
app.js ──────────┐
├──> user.js
user-action.js ──┘
Pertanyaannya:
Apakah
user.jsdijalankan dua kali?
Jawabannya:
Tidak.
23. Module di-load dan dieksekusi sekali
Ketika module pertama kali dibutuhkan:
app.js
│
▼
user.js
Browser mengambil:
user.js
kemudian module tersebut dieksekusi.
Kalau kemudian module lain juga mengatakan:
user-action.js
│
▼
user.js
browser mengetahui:
“Oh,
user.jssudah pernah di-load.”
Maka tidak perlu menjalankan ulang module tersebut.
24. Visualisasinya
Bayangkan:
app.js
/ \
/ \
▼ ▼
user.js user-action.js
│
│ import
▼
user.js
Ada dua panah menuju:
user.js
Tetapi hanya ada satu module instance yang digunakan.
Jadi bukan:
user.js → execute
user.js → execute lagi
melainkan:
user.js
│
▼
execute sekali
│
▼
hasil/module digunakan
oleh semua importer
25. Contoh menggunakan console.log
Dosen membuktikannya dengan:
console.log("user.js file executing");
Misalnya:
// user.ts
console.log("user.js file executing");
export default class User {
...
}
Kemudian user.ts digunakan oleh:
app.ts
dan:
user-action.ts
Walaupun ada dua import:
app.ts
│
└── import user.js
user-action.ts
│
└── import user.js
output:
user.js file executing
hanya muncul satu kali.
26. Kenapa ini penting?
Karena bayangkan kalau module selalu dieksekusi setiap kali di-import.
Misalnya sebuah module punya:
let counter = 0;
counter++;
console.log(counter);
Kalau module dijalankan berkali-kali, state-nya bisa kacau.
Tetapi module system memastikan satu module yang sama tidak terus-menerus dieksekusi ulang hanya karena digunakan dari banyak tempat.
Secara sederhana:
module pertama kali diminta
↓
load
↓
execute
↓
simpan/cache module
↓
import berikutnya
↓
gunakan module yang sudah ada
27. “Downloaded once” dan “executed once”
Dosen menggunakan dua konsep:
Download
Browser tidak perlu mengunduh file yang sama berkali-kali hanya karena beberapa module membutuhkannya.
Execute
Code module juga tidak dieksekusi ulang setiap kali di-import.
Jadi secara sederhana:
First import
↓
Download
↓
Execute
↓
Available
Second import
↓
Sudah tersedia
↓
Tidak execute ulang
28. Ini mirip seperti singleton module
Untuk pemahaman pemula, kamu bisa membayangkannya seperti:
user.js
│
▼
Satu module instance
│
├──────────> app.js
│
└──────────> user-action.js
Bukan:
user.js instance #1 → app.js
user.js instance #2 → user-action.js
Catatan: ini analogi untuk memahami perilaku module, bukan berarti ES6 module secara formal sama persis dengan pola Singleton Design Pattern.
29. Ringkasan semua bentuk import
Sekarang kita kumpulkan.
A. Named import
import { User } from "./user.js";
Untuk:
export class User {}
B. Multiple named imports
import { User, Product, Order }
from "./models.js";
Untuk:
export class User {}
export class Product {}
export class Order {}
C. Import semua named exports
import * as actions
from "./user-action.js";
Kemudian:
actions.createUser();
actions.getUsers();
D. Named import dengan alias
import { User as Person }
from "./user.js";
Kemudian:
new Person(...);
E. Default import
Kalau:
export default class User {}
Import:
import User from "./user.js";
F. Default import dengan nama berbeda
Kalau:
export default class User {}
bisa:
import Person from "./user.js";
atau:
import MyUser from "./user.js";
Nama lokal bebas.
G. Default + named sekaligus
Kalau:
export default class User {}
export const pi = 3.14;
Import:
import User, { pi }
from "./user.js";
30. Tabel cepat untuk mengingat
| Export | Import |
|---|---|
export const x = ... | import { x } from ... |
export function x() {} | import { x } from ... |
export class User {} | import { User } from ... |
export default class User {} | import User from ... |
| banyak named export | import * as something from ... |
| named + alias | import { User as Person } from ... |
31. Perbedaan {} dan tanpa {}
Ini wajib banget diingat.
Ada {}
import { User } from "./user.js";
→ Named import
Tidak ada {}
import User from "./user.js";
→ Default import
* as
import * as actions from "./user-action.js";
→ Ambil semua named exports sebagai actions.
Mnemonic:
{} → named
tanpa {} → default
* as → semua named
32. Perbedaan alias pada named dan default
Ini juga penting.
Named export
export class User {}
Import:
import { User as Person } from "./user.js";
Di sini:
User
↓
nama asli export
Person
↓
nama lokal
Default export
export default class User {}
Import:
import Person from "./user.js";
Tidak perlu:
as
Karena nama lokal memang boleh langsung kita tentukan.
33. Cara berpikir yang paling mudah
Jangan menghafal syntax secara terpisah. Pikirkan pertanyaan ini:
Pertanyaan 1
Apa yang diekspor?
export ...
Pertanyaan 2
Apakah named atau default?
export X
↓
named
export default X
↓
default
Pertanyaan 3
Bagaimana cara mengambilnya?
named
↓
{ X }
default
↓
X
semua named
↓
* as something
34. Hubungkan dengan lecture sebelumnya
Lecture sebelumnya:
Namespace
mengajarkan:
namespace App {
export class User {}
}
Kemudian kita membutuhkan:
/// <reference path="..." />
Sekarang ES6 Modules:
export class User {}
dan:
import { User } from "./user.js";
Jadi transisinya:
NAMESPACE
│
│
├── namespace App
├── /// reference
└── outFile / bundle
│
▼
ES6 MODULES
│
├── export
├── import
├── default export
├── named export
└── browser memahami dependency
35. Inti lecture dalam satu contoh
Misalkan kita punya:
// user.ts
console.log("user.js executing");
export default class User {
constructor(
public name: string,
public age: number
) {}
}
export const pi = 3.14;
File tersebut memiliki:
1 default export
→ User
1 named export
→ pi
Kemudian:
// user-action.ts
import User from "../models/user.js";
const userList: User[] = [];
export function createUser(name: string, age: number) {
const user = new User(name, age);
userList.push(user);
return user;
}
export function getUsers() {
return userList;
}
Kemudian:
// app.ts
import * as actions
from "./user-utils/user-action.js";
import User from "./models/user.js";
const user = new User("John", 28);
actions.createUser("Mary", 30);
console.log(actions.getUsers());
Perhatikan:
app.ts
│
├── import User ──────────┐
│ │
└── import actions │
│ │
▼ │
user-action.ts │
│ │
│ import User │
▼ │
user.ts ◄────────┘
user.ts hanya dieksekusi sekali, meskipun dua module membutuhkannya.
36. Kesimpulan paling penting
Kalau kamu hanya mengingat 7 hal dari lecture ini, ingat ini:
1. Named export
export const pi = 3.14;
di-import dengan:
import { pi } from "./file.js";
2. Default export
export default class User {}
di-import dengan:
import User from "./file.js";
3. Named import bisa diberi alias
import { User as Person } from "./file.js";
Kemudian:
new Person(...)
4. import * as
import * as actions from "./file.js";
berarti:
Ambil semua named exports dan kumpulkan di bawah
actions.
Kemudian:
actions.createUser();
actions.getUsers();
5. * tidak mengambil sesuatu yang tidak di-export
const secret = 123;
export const pi = 3.14;
import * as x hanya mendapatkan:
x.pi
bukan:
x.secret
6. Satu file hanya punya satu default export
Boleh:
export default class User {}
export const pi = 3.14;
export function hello() {}
Tetapi tidak boleh punya dua export default.
7. Module yang sama tidak dieksekusi ulang
Jika:
app.js ──────────┐
├──> user.js
user-action.js ──┘
maka user.js tetap:
download/load → execute → sekali
Import berikutnya menggunakan module yang sudah dimuat, bukan menjalankannya lagi.
🧠 Mnemonic super singkat
Coba hafalkan pola ini:
EXPORT
│
├── named
│ ↓
│ import { X }
│
└── default
↓
import X
IMPORT
│
├── { X }
│ → named
│
├── X
│ → default
│
└── * as X
→ semua named exports
Dan satu aturan terakhir:
export= “Saya menyediakan.”import= “Saya membutuhkan.”default= “Ini adalah export utama saya.”* as= “Berikan semua named export kepada saya dalam satu nama.”