- 모듈이 도입된 배경
- "모든 것을 공유하는" 방식은 가장 에러가 발생하기 쉬운 부분이라 지양
- es6 이전까지 자바스크립트 파일에 정의된 어플리케이션의 모든 부분들은 하나의 전역 스코프 공유
- 이름 충돌
- 보안상 우려
- 모듈의 자바스크립트 코드는 자동으로 strict 모드에서 실행
- 모듈의 최상위 수준에서 만들어진 변수는 공용 전역 스코프에 자동으로 추가되지 않는다.
- 모듈의 최상위 수준 스코프 내에만 존재
- 모듈 외부에서 이용하기 위해서는 해당 변수나 함수 같은 요소를 모듈에서 Export
- 다른 모듈의 바인딩을 Import 할 수도 있다.
- 특징
- 최상위 수준에서 모듈의 this 값은 undefined이다.
- 모듈은 코드 내에 초창기 브라우저의 부산물인 HTML 스타일 주석을 허용하지 않는다.
- 결론
- 진정한 힘은 파일 내에 정의된 모든 것 중 필요한 바인딩만 익스포트하고 임포트하는 기능에 있다.
- export
// 데이터 익스포트
export var color = "red";
export let name = "Nicholas";
export const magicNumber = 7;
// 함수 익스포트
export function sum(num1, num2) {
return sum1 + sum2;
}
// 클래스 익스포트
export class Rectangle {
constructor(length, width) {
this.length = length;
this.width = width;
}
// ...
}
// this is private
function subtract(num1, num2) {
return num1 - num2;
}
// function definition
function multiply(num1, num2) {
return num1 * num2;
}
// export the function defined above
export { multiply };
- import
import { identifier1, identifier2 } from "./example.js";
-
{ }
- 주어진 모듈로부터 임포트할 바인딩을 가리킨다.
- "./example.js" called module specifier(모듈 명시자)
-
Difference btw script and Node.js
- 스크립트 내에서는 확장자까지 포함
- Node.js
- example >> package
- example >> ./example.js
-
!구조 분해된 객체가 아니다.
-
특징
- 모듈로부터 바인딩 임포트 시
- 바인딩은 const를 사용하여 정의한 것처럼 동작
- 같은 이름의 또 다른 변수 정의x
- 같은 이름의 또 다른 바인딩 임포트x
- import 문 앞에서 그 식별자를 사용할 수 없으며 변경할 수도 없다는 의미
- 같은 이름의 또 다른 변수 정의x
- 바인딩은 const를 사용하여 정의한 것처럼 동작
- 모듈로부터 바인딩 임포트 시
import { sum } from "./example.js";
console.log(sum(1, 2)); // 3
sum = 1; // Error!
// 여러 개 임포트
import { sum, multiply, magicNumber } from "./example.js";
console.log(sum(1, magicNumber)); // 8
console.log(multiply(1, 2)); // 2
import * as example from "./example.js";
console.log(example.sum(1, example.magicNumber)); // 8
console.log(example.multiply(1, 2)); // 2
- This kind of import is called "Namespace Import"
- int example.js, there is no such object, example, but a name space object created instead
- modules are excuted once although you import them several on a script
- The module instantiated is registered in memory and everytime another "import" refers the module, it was referred from the memory
import { sum } from "./example.js";
import { multiply } from "./example.js";
import { magicNumber } from "./example.js";
- there are 3 import code referring "./example.js", but example.js is executed only once.
! 최상위 수준에서만 import, export 할 수 있다.
function tryImport() {
import flag from "./example.js"; // 문법 에러
}
if (true)
export flag; // 문법 에러
export var name = "Nicholas";
export function setName(newName) {
name = newName;
}
import { name, setName } from "./example.js";
name = "Nicholas"; // 에러 발생!
function sum(num1, num2) {
return num1 + num2;
}
export { sum as add };
import { add as sum } from "./example.js";
- default
- 모듈 당 한개만 가능
function sum (num1, num2) {
return num1 + num2;
}
export default sum;
////
export { sum as default };
// 기본 값 임포트
import sum from "./example.js";
////
export let color = "red";
export default function(num1, num2) {
return num1 + num2;
}
////
import sum, { color } from "./example.js";
- when you import default
- don't use { }
- you can just write
- import sum from "./example.js";
- you can just write
- don't use { }
- default has to be placed at the beginning of import statement.
import { default as sum, color } from "example";
- or you can import
import { sum } from "./example.js";
export { sum }
// or
export { sum } from "./example.js";
// or
export { sum as add } from "./example.js";
// 모두 익스포트
export * from "./example.js";
- 모듈 내부에서도 Array와 Object 같이 전역 스코프로 공유되는 내장 객체에 접근하고 그 정의를 변경할 수 있으며, 만약 그 객체가 변경이 되면 다른 모듈에도 반영된다.
// 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);
};
// app.js
import "./example.js"; // 바인딩 없이 import
let colors = ["red", "green", "blue"];
let items = [];
items.pushAll(colors);
! 바인딩 없는 임포트문은 대부분 polyfill이나 shim에 사용된다.
-
<script> 엘리먼트의 src 속성에 명시
-
src 속성 없이 <script> 엘리먼트에 인라인으로 자바스크립트 코드 내장
-
워커를(웹 워커 서비스 워커)를 실행하여 자바스크립트 코드 파일을 로딩
- 서비스워커 정체가 뭐니? from 임택
- "module"은 "text/javascript"처럼 콘텐츠 타입이 아니라는 것에 주의해야 한다.
- 반면에 모듈 자바스크립트 파일은 스크립트 자바스크립트 파일과 같은 콘텐츠 타입으로 제공되므로,
- 콘텐츠 타입인지 여부로만 구별하는 것은 불가능하다.
- 또한, 브라우저는 type이 인식되지 않는 경우 <script> 를 무시한다.
- 즉, 모듈을 지원하지 않는 브라우저는 <script type="module"> 을 자동으로 무시하기 때문에
- 하위 호환에 문제가 없다.
- 즉, 모듈을 지원하지 않는 브라우저는 <script type="module"> 을 자동으로 무시하기 때문에
- 또한, 브라우저는 type이 인식되지 않는 경우 <script> 를 무시한다.
- 콘텐츠 타입인지 여부로만 구별하는 것은 불가능하다.
- 반면에 모듈 자바스크립트 파일은 스크립트 자바스크립트 파일과 같은 콘텐츠 타입으로 제공되므로,
- <script type="module"> 은 기본적으로 defer 속성
- defer
- src 속성을 만나자마자 다운로드 시작하지만
- 문서가 완전히 파싱될 때 까지는 실행되지 않는다.
- src 속성을 만나자마자 다운로드 시작하지만
- defer
<!-- 먼저 실행됨 -->
<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>
- 각 모듈은 하나 이상의 다른 모듈에서 import되 수 있는데
- 이는 로딩 순서를 복잡하게 만든다.
- 이러한 이유로 모든 import 문을 식별하기 위해 모듈이 먼저 완전하게 파싱되어야 한다.
- 그리고 나서 각 import 문은 패치(fetch, from network cache)를 시작
- 모든 import의 리소스들이 로딩되고 실행될 때까지 어떤 모듈도 실행되지 않는다.
- 그리고 나서 각 import 문은 패치(fetch, from network cache)를 시작
- 이러한 이유로 모든 import 문을 식별하기 위해 모듈이 먼저 완전하게 파싱되어야 한다.
- 이는 로딩 순서를 복잡하게 만든다.
- async 사용
- 일반적으로 다운로드 후 바로 실행
- 차이점은
- 모듈의 모든 import 리소스가 모듈 실행 전에 다운로드 된다는 것이다.
- 이는 모듈 실행에 필요한 모든 리소스가 반드시 먼저 다운로드 된다는 걸 의미
- 그러나~
- 언제 실행될지 확실히 알 수 있지는 않다.
- 그러나~
- 이는 모듈 실행에 필요한 모든 리소스가 반드시 먼저 다운로드 된다는 걸 의미
- 모듈의 모든 import 리소스가 모듈 실행 전에 다운로드 된다는 것이다.
- 차이점은
- 일반적으로 다운로드 후 바로 실행
<!-- don't know when to be excuted -->
<script type="module" async src="module1.js"></script>
<script type="module" async src="module2.js"></script>
- 웹 워커와 서비스 워커 같은 워커는 웹 페이지 컨텍스트의 외부에서 자바스크립트 코드를 실행한다.
- 새 워커를 만든다.
- 새로운 Worker 인스턴스를 만들고
- 자바스크립트 파일의 위치를 전달한다는 의미이다.
- 새로운 Worker 인스턴스를 만들고
// 스크립트처럼 script.js를 로딩
let worker = new Worker("script.js");
// 모듈처럼 module.js를 로딩
let worker = new Worker("module.js", { type: "module"});
- 워커 스크립트는 웹 페이지와 같은 출처에서만 로딩
- but 워커 모듈은 그러한 제한이 없다.
- 워커 모듈도 기본적으로 동일한 제약을 가지지만,
- 접근을 허용하는 적절한 Cross-Origin Resource Sharing(CORS) 헤더를 가진 파일은 로드할 수 있다.
- 워커 모듈도 기본적으로 동일한 제약을 가지지만,
- but 워커 모듈은 그러한 제한이 없다.
- 스크립트에서는 워커에 추가적인 스크립트를 로딩하기 위해
- self.importScripts() 메서드를 사용할 수 있지만
- But 워커 모듈에서는 self.importScripts()가 항상 실패한다.
- 모듈 명시자로 (문자열 "./example.js") 상대 경로 사용 중
- 사용 가능한 경로
- 루트 /
- 현재 디렉토리 ./
- 부모 디렉토리 ../
- URL 형식
<script type="module">
// 유효하지 않음 /, ./, ../ 로 시작해야 함
import { first } from "example.js";
import { second } from "example/index.js";
</script>
- 이 처럼 정상적으로 보이는 일부 모듈 명시자도 브라우저에서 유효하지 않으며, 에러를 발생 시킨다.
~~~ 마무으리~~~~