- 자동으로 strict 모드에서 실행
- 모듈의 최상위 수준에서 만들어진 변수는 모듈 최상의 스코프 내에만 존재
- 최상위 수준에서 모듈의 this는 undefined
- HTML 스타일 주석을 허용하지 않음
// 데이터 익스포트
export var color = "red";
export let name = "Nicholas";
export const magicNumber = 7;
// 함수 익스포트
export function sum(num1, num2) {
return num1 + num2;
}
// 클래스 익스포트
export class Rectangle {
constructor(length, width) {
...
}
}
// 이 함수는 모듈에 비공개
function subtract(num1, num2) {
...
}
//함수 정의
function multiply(num1, num2) {
...
}
// 위에서 정의한 함수를 익스포트
export {multiply};
import {identifier1, identifier2} from "./example.js";
import * as example from "./example.js";
- 바인딩은 const를 사용한것 처럼 동작.
- 같은 이름의 또 다른 변수를 정의할 수 없고, import 문 앞에서 그 식별자를 사용할수 없고 값 변경도 불가능.
import { sum } from "./example.js";
import { multiply } from "./example.js";
import { magicNumber } from "./example.js";
- 모듈은 import문에서 모듈을 사용한 회수와 상관없이 한 번만 실행 된다.
13.3.4 임포트 바인딩의 특이한 점
단순히 일반 변수처럼 원본의 바인딩을 참조하지 않고 변수, 함수 및 클래스에 대한 읽기 전용 바인딩을 만든다. 바인딩을 임포트한 모듈은 바인딩의 값을 변경할 수 없지만, 식별자를 익스포트한 모듈은 가능하다.
export var name = "Nicholas";
export function setName(newName) {
name = newName;
}
import { name, setName } from "./example.js";
console.log(name); // Nicholas
setName("Greg");
console.log(name); // Greg
name = "Nicholas"; //에러 발생!
name이 익스포트된 name 식별자를 위한 지역 스코프 내의 이름이기 때문에 이런 동작을 함.
function sum(num1, num2) {}
export {sum as add};
//import 할 때
import { add } from "./example.js";
import {add as sum} from "./example.js";
console.log(typeof add); // "undefined"
console.log(sum(1,2));
1.
export default function(num1, num2) {}
2.
function sum(num1, num2) {}
export default sum;
3.
function sum(num1, num2) {}
export {sum as default};
import sum from "./example.js";
import sum, {color} from "./example.js";
import {default as sum, color} from "./example.js";
1.
import {sum} from "./example.js";
export {sum}
2.
export {sum} from "./example.js";
3.
export { sum as add } from "./example.js";
4.
export * from "./example.js";
어떤 모듈은 아무것도 익스포트하지 않는 대신, 전역 스코프 객체만 수정한다.
모듈 내의 최 사우이 수준 변수와 함수, 클래스는 자동으로 전역 스코프가 되지 않는데, 이것이 모듈내에서 전역 스코프에 접근할 수 없다는 의미는 아니다.
// 익스포트나 임포트 없는 모듈 코드
Array.prototype.pushAll = function(items) {
//items는 배열이어야 함.
if(!Array.isArray(items)) {
throw new TypeError("Argument must be an array");
}
// 내장 push()와 전개 연산자를 사용
return this.push(...items);
}
import "./example.js";
let colors=["red", "green", "blue"];
let itmes = [];
items.pushAll(colors);
13.8.1 웹브라우저에서 모듈 사용하기
웹 브라우저에서 스크립트를 로드하는 옵션
- <script> 엘리먼트의 src 속성에 로드할 코드의 위치를 명시
- src 속성없이 <script>엘리먼트에 인라인으로 js 코드를 내장
- 워커(웹 워커나 서비스 워커)를 실행하여 js 코드 파일을 로딩
<script>로 모듈 사용하기
<!-- 자바스크립트 파일 모듈 로딩-->
<script type="module" src="module.js"></script>
<!-- 인라인 모듈 포함 -->
<script type="module">
import {sum} from "./example.js";
let result = sum(1,2);
</script>
웹브라우저에서 모듈이 로드되는 순서
모듈에서는 스크립트와 달리, 올바른 실행을 보장하기 위해 먼저 로딩되어야 하는 파일을 지정하려는 목적으로 import 를 사용할 수 있다.
이를 위해서 <script type="module">은 항상 defer속성이 적용된 것처럼 동작한다.
→ HTML파서가 <script type="module"> 내의 src속성을 만나자마자 다운로드되기 시작하지만, 문서가 완전히 파싱될 때까지 실행되지 않는다.
→ HTML 파일 내에 <script type="module"> 엘리먼트가 위치한 순서대로 실행된다.
<!-- 먼저 실행됨-->
<script type="module" src="module1.js"></script>
<!-- 두 번째로 실행됨-->
<script type="module">
import {sum} from "./example.js";
let result = sum(1,2);
</script>
<!-- 세 번째로 실행됨-->
<script type="module" src="module2.js"></script>
- module1.js를 다운로드하고 파싱
- module1.js에 있는 import 리소스를 재귀적으로 다운로드하고 파싱
- 인라인 모듈을 파싱
- 인라인 모듈 안에 있는 import 리소스를 재귀적으로 다운로드하고 파싱
- module2.js를 다운로드하고 파싱
- module2.js에 있는 import 리소스를 재귀적으로 다운로드하고 파싱
로드가 완료되면, 문서가 완전히 파싱될 때까지 아무것도 실행되지 않는다. 문서가 완전히 파싱된 후에, 다음과 같이 동작한다.
- module1.js의 import 리소스를 재귀적으로 실행
- module1.js 실행
- 인라인 모듈의 import 리소스를 재귀적으로 실행
- 인라인 모듈 실행
- module2.js의 import 리소스를 재귀적으로 실행
- module2.js 실행
웹 브라우저에서 비동기 모듈 로드하기
<script async src="~~~">
파일 다운로드와 파싱이 완료되자마자 스크립트가 바로 실행된다.
<script async src="./src1.js">
<script async src="./src2.js">
각각 파일 다운로드와 파싱이 완료되면 스크립트가 실행되지만 꼭 순서대로 실행을 한다는 보장은 없다.
<!-- 모듈에도 적용가능, 어떤 것이 먼저 실행될지는 알 수 없음 -->
<script type="module" async src="module1.js"></script>
<script type="module" async src="module2.js"></script>
두 모듈 파일은 비동기로 로딩된다. 하지만 뭐가 먼저 실행될지는 알 수 없다. module1 다운로드가 먼저 완료되면 먼저 실행되고, module2도 마찬가지이다.
[참고 script의 async와 defer속성]
워커에서 모듈 로드하기
//스크립트처럼 script.js 를 로딩
let worker = new Worker("script.js");
//모듈처럼 module.js를 로딩
let worker = new Worker("module.js', {type: "module"});
워커 모듈이 워커 스크립트와 다른 점
- 워커 스크립트는 웹 페이지와 같은 출처에서만 로딩될 수 있지만, 워커 모듈은 그런 제한이 없다. (적절한 CORS헤더를 가진 파일은 로드할 수 있다)
- 워커 스크립트에서는 워커에 추가적인 스크립트를 로딩하기 위해 self.importScripts() 메서드를 사용할 수 있지만, 워커 모듈에서는 그 대신 import를 사용해야만 하기 때문에 self.importScripts() 가 항상 실패한다.
브라우저 모듈 명시자 결의안
- 루트 디렉토리를 의미하는 / 로 시작
- 현재 디렉토리를 의미하는 ./ 로 시작
- 부모 디렉토리를 의미하는 ../ 로 시작
- URL 형식