|
55 | 55 |
|
56 | 56 |
|
57 | 57 | > [!NOTE] |
58 | | -> Last updated 2025/04/06 |
| 58 | +> updated 2025/04/11 |
59 | 59 |
|
| 60 | +### Different Types of Imports and Exports |
60 | 61 |
|
| 62 | +> an overview of how different imports and exports behave |
61 | 63 |
|
| 64 | +```javascript |
| 65 | +// These give you a live reference: |
| 66 | +import { thing } from './module.js'; |
| 67 | +import { thing as otherName } from './module.js'; |
| 68 | +import * as module from './module.js'; |
| 69 | +const module = await import('./module.js'); |
| 70 | + |
| 71 | +// This assigns the current value: |
| 72 | +let { thing } = await import('./module.js'); |
| 73 | +``` |
| 74 | + |
| 75 | +## The Special Case of `export default` |
| 76 | + |
| 77 | + The `export default` syntax has different semantics than named exports. |
| 78 | + |
| 79 | +```javascript |
| 80 | +// module.js |
| 81 | +let thing = 'initial'; |
| 82 | +export { thing }; |
| 83 | +export default thing; |
| 84 | +setTimeout(() => { |
| 85 | + thing = 'changed'; |
| 86 | +}, 500); |
| 87 | +``` |
| 88 | + |
| 89 | +> Export Default Behavior |
| 90 | +
|
| 91 | + |
| 92 | +### Why the Difference? |
| 93 | + |
| 94 | +The reason for this behavior is that `export default` allows exporting values directly: |
| 95 | + |
| 96 | +```javascript |
| 97 | +export default 'hello!'; // This works |
| 98 | +export { 'hello!' as thing }; // This doesn't work |
| 99 | +``` |
| 100 | + |
| 101 | +## The Third Way: `export { thing as default }` |
| 102 | + |
| 103 | +There's another way to export a default that behaves differently: |
| 104 | + |
| 105 | +```javascript |
| 106 | +// module.js |
| 107 | +let thing = 'initial'; |
| 108 | +export { thing, thing as default }; |
| 109 | +setTimeout(() => { |
| 110 | + thing = 'changed'; |
| 111 | +}, 500); |
| 112 | +``` |
| 113 | + |
| 114 | +This method maintains the live reference, unlike `export default thing`. |
| 115 | + |
| 116 | +### Special Case: `export default function` |
| 117 | + |
| 118 | +Functions get special treatment: |
| 119 | + |
| 120 | +```javascript |
| 121 | +// module.js |
| 122 | +export default function thing() {} |
| 123 | +setTimeout(() => { |
| 124 | + thing = 'changed'; |
| 125 | +}, 500); |
| 126 | + |
| 127 | +// main.js |
| 128 | +import thing from './module.js'; |
| 129 | +setTimeout(() => { |
| 130 | + console.log(thing); // "changed" |
| 131 | +}, 1000); |
| 132 | +``` |
| 133 | + |
| 134 | +> Function Export Behavior |
| 135 | +
|
| 136 | + |
| 137 | +## Circular Dependencies and Hoisting |
| 138 | + |
| 139 | +### Function Hoisting Behavior |
| 140 | + |
| 141 | +```javascript |
| 142 | +thisWorks(); // This runs fine |
| 143 | +function thisWorks() { |
| 144 | + console.log('yep, it does'); |
| 145 | +} |
| 146 | +``` |
| 147 | + |
| 148 | +> [!NOTE] |
| 149 | +> Function declarations are hoisted, but other declarations aren't: |
| 150 | +
|
| 151 | +```javascript |
| 152 | +assignedFunction(); // Doesn't work |
| 153 | +new SomeClass(); // Doesn't work |
| 154 | + |
| 155 | +const assignedFunction = function() { |
| 156 | + console.log('nope'); |
| 157 | +}; |
| 158 | +class SomeClass {} |
| 159 | +``` |
| 160 | + |
| 161 | +### Circular Dependencies Example |
| 162 | + |
| 163 | +```javascript |
| 164 | +// main.js |
| 165 | +import { foo } from './module.js'; |
| 166 | +foo(); |
| 167 | +export function hello() { |
| 168 | + console.log('hello'); |
| 169 | +} |
| 170 | +``` |
| 171 | + |
| 172 | +```javascript |
| 173 | +// module.js |
| 174 | +import { hello } from './main.js'; |
| 175 | +hello(); |
| 176 | +export function foo() { |
| 177 | + console.log('foo'); |
| 178 | +} |
| 179 | +``` |
| 180 | + |
| 181 | +This works due to hoisting. However, changing to arrow functions breaks it: |
| 182 | + |
| 183 | +```javascript |
| 184 | +// main.js |
| 185 | +import { foo } from './module.js'; |
| 186 | +foo(); |
| 187 | +export const hello = () => console.log('hello'); |
| 188 | +``` |
| 189 | + |
| 190 | +```javascript |
| 191 | +// module.js |
| 192 | +import { hello } from './main.js'; |
| 193 | +hello(); |
| 194 | +export const foo = () => console.log('foo'); |
| 195 | +``` |
| 196 | + |
| 197 | +## Summary |
| 198 | + |
| 199 | +The complete behavior overview: |
| 200 | + |
| 201 | +```javascript |
| 202 | +// Live references: |
| 203 | +import { thing } from './module.js'; |
| 204 | +import { thing as otherName } from './module.js'; |
| 205 | +import * as module from './module.js'; |
| 206 | +const module = await import('./module.js'); |
| 207 | + |
| 208 | +// Value copy: |
| 209 | +let { thing } = await import('./module.js'); |
| 210 | + |
| 211 | +// Live reference exports: |
| 212 | +export { thing }; |
| 213 | +export { thing as otherName }; |
| 214 | +export { thing as default }; |
| 215 | +export default function thing() {} |
| 216 | + |
| 217 | +// Value exports: |
| 218 | +export default thing; |
| 219 | +export default 'hello!'; |
| 220 | +``` |
| 221 | + |
| 222 | +_Note: The original article was written by Jake Archibald, with contributions from the V8 team members Toon Verwaest, Marja Hölttä, and Mathias Bynens, as well as Dave Herman and Daniel Ehrenberg._ |
| 223 | + |
| 224 | +### Best Practices |
| 225 | + |
| 226 | +1. Avoid circular dependencies whenever possible |
| 227 | +2. Be aware of the difference between value exports and reference exports |
| 228 | +3. Consider using `export { thing as default }` instead of `export default thing` when you need to maintain live bindings |
| 229 | +4. Remember that `export default function` is a special case that maintains references |
| 230 | + |
| 231 | + |
| 232 | +```mermaid |
| 233 | +graph TD |
| 234 | + subgraph "Export Types" |
| 235 | + A["Named Export<br>export { thing }"] -->|"Live Reference"| D["Import { thing }"] |
| 236 | + A -->|"Live Reference"| E["Import { thing as otherName }"] |
| 237 | + A -->|"Live Reference"| F["Import * as module"] |
| 238 | + A -->|"Live Reference"| G["await import('./module.js')"] |
| 239 | + |
| 240 | + B["Default Export<br>export default thing"] -->|"Value Copy"| H["Import default"] |
| 241 | + |
| 242 | + C["Function Export<br>export default function(){}"] -->|"Live Reference"| I["Import default function"] |
| 243 | + |
| 244 | + J["Named as Default<br>export { thing as default }"] -->|"Live Reference"| K["Import default"] |
| 245 | + end |
| 246 | + |
| 247 | + subgraph "Import Types" |
| 248 | + D |
| 249 | + E |
| 250 | + F |
| 251 | + G -->|"Value Copy"| L["let { thing } = await import()"] |
| 252 | + G -->|"Live Reference"| M["const module = await import()"] |
| 253 | + H |
| 254 | + I |
| 255 | + K |
| 256 | + end |
| 257 | + |
| 258 | + subgraph "Circular Dependencies" |
| 259 | + N["Function Declaration<br>export function thing(){}"] -->|"Works due to hoisting"| O["Circular Import"] |
| 260 | + P["Arrow Function<br>export const thing = ()=>{}"] -->|"Fails - no hoisting"| Q["Circular Import Fails"] |
| 261 | + end |
| 262 | + |
| 263 | + style A fill:#d4f1f9,stroke:#333 |
| 264 | + style B fill:#ffdfba,stroke:#333 |
| 265 | + style C fill:#ffdfba,stroke:#333,stroke-dasharray: 5 5 |
| 266 | + style J fill:#d4f1f9,stroke:#333,stroke-dasharray: 5 5 |
| 267 | + style N fill:#d8f8e1,stroke:#333 |
| 268 | + style P fill:#ffbaba,stroke:#333 |
| 269 | +``` |
| 270 | + |
| 271 | +#### Named exports (blue boxes) create live references when imported through any import syntax |
| 272 | + |
| 273 | +Regular named exports maintain live bindings to the original variables |
| 274 | +The special export { thing as default } syntax also maintains live binding |
| 275 | + |
| 276 | + |
| 277 | +#### Default exports (orange boxes) behave differently: |
| 278 | + |
| 279 | +Regular export default thing creates a value copy (not a reference) |
| 280 | +The special case export default function() {} maintains a live reference |
| 281 | + |
| 282 | + |
| 283 | +#### Circular dependencies behavior: |
| 284 | + |
| 285 | +Function declarations (green box) work in circular dependencies due to hoisting |
| 286 | +Arrow functions and const declarations (red box) fail in circular dependencies |
62 | 287 |
|
63 | 288 | <a name="es6-exports--imports-cheat-sheet"></a> |
64 | 289 | ### ES6 exports / imports cheat sheet |
|
0 commit comments