Skip to content

Latest commit

 

History

History
417 lines (280 loc) · 10.5 KB

ch13.Module.Taek.md

File metadata and controls

417 lines (280 loc) · 10.5 KB

ch 13 모듈로 캡슐화 하기

  • 모듈이 도입된 배경
    • "모든 것을 공유하는" 방식은 가장 에러가 발생하기 쉬운 부분이라 지양
    • es6 이전까지 자바스크립트 파일에 정의된 어플리케이션의 모든 부분들은 하나의 전역 스코프 공유
      • 이름 충돌
      • 보안상 우려

13.1 모듈이란?

  • 모듈의 자바스크립트 코드는 자동으로 strict 모드에서 실행
  • 모듈의 최상위 수준에서 만들어진 변수는 공용 전역 스코프에 자동으로 추가되지 않는다.
    • 모듈의 최상위 수준 스코프 내에만 존재
    • 모듈 외부에서 이용하기 위해서는 해당 변수나 함수 같은 요소를 모듈에서 Export
    • 다른 모듈의 바인딩을 Import 할 수도 있다.
  • 특징
    • 최상위 수준에서 모듈의 this 값은 undefined이다.
    • 모듈은 코드 내에 초창기 브라우저의 부산물인 HTML 스타일 주석을 허용하지 않는다.
  • 결론
    • 진정한 힘은 파일 내에 정의된 모든 것 중 필요한 바인딩만 익스포트하고 임포트하는 기능에 있다.

13.2 익스포트 기본

  • 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 };

13.3 임포트 기본

  • 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 문 앞에서 그 식별자를 사용할 수 없으며 변경할 수도 없다는 의미

13.3.1 한 개의 바인딩만 임포트하기

import { sum } from "./example.js";

console.log(sum(1, 2)); // 3
sum = 1; // Error!

13.3.2 여러 개의 바인딩 임포트하기

// 여러 개 임포트
import { sum, multiply, magicNumber } from "./example.js";
console.log(sum(1, magicNumber)); // 8
console.log(multiply(1, 2)); // 2

13.3.3 모듈 전체 임포트하기

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; // 문법 에러

13.3.4 임포트 바인딩의 특이한 점

export var name = "Nicholas";
export function setName(newName) {
    name = newName;
}
import { name, setName } from "./example.js";
name = "Nicholas"; // 에러 발생!

13.4 익스포트와 임포트에 새로운 이름 사용하기

function sum(num1, num2) {
    return num1 + num2;
}

export { sum as add };
import { add as sum } from "./example.js";

13.5 모듈의 기본값

  • default
    • 모듈 당 한개만 가능

13.5.1 기본 값 익스포트하기

function sum (num1, num2) {
    return num1 + num2;
}

export default sum;

////
export { sum as default };

13.5.2 기본 값 임포트하기

// 기본 값 임포트
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";
  • default has to be placed at the beginning of import statement.
import { default as sum, color } from "example";
  • or you can import

13.6 바인딩을 다시 익스포트하기

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";

13.7 바인딩 없이 임포트하기

  • 모듈 내부에서도 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에 사용된다.

13.8 모듈 로드하기

13.8.1 웹 브라우저에서 모듈 사용하기

  • <script> 엘리먼트의 src 속성에 명시

  • src 속성 없이 <script> 엘리먼트에 인라인으로 자바스크립트 코드 내장

  • 워커를(웹 워커 서비스 워커)를 실행하여 자바스크립트 코드 파일을 로딩

<script>**로 모듈 사용하기
<script type="module" src="module.js"></script> <script type="module"> import { sum } from "./example.js"; let result = sum(1 ,2); </script>
  • "module"은 "text/javascript"처럼 콘텐츠 타입이 아니라는 것에 주의해야 한다.
    • 반면에 모듈 자바스크립트 파일은 스크립트 자바스크립트 파일과 같은 콘텐츠 타입으로 제공되므로,
      • 콘텐츠 타입인지 여부로만 구별하는 것은 불가능하다.
        • 또한, 브라우저는 type이 인식되지 않는 경우 <script> 를 무시한다.
          • 즉, 모듈을 지원하지 않는 브라우저는 <script type="module"> 을 자동으로 무시하기 때문에
            • 하위 호환에 문제가 없다.
웹 브라우저에서 모듈이 로드되는 순서
  • <script type="module"> 은 기본적으로 defer 속성
    • defer
      • src 속성을 만나자마자 다운로드 시작하지만
        • 문서가 완전히 파싱될 때 까지는 실행되지 않는다.
<!-- 먼저 실행됨 -->
<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의 리소스들이 로딩되고 실행될 때까지 어떤 모듈도 실행되지 않는다.
웹 브라우저에서 비동기 모듈 로드하기
  • async 사용
    • 일반적으로 다운로드 후 바로 실행
      • 차이점은
        • 모듈의 모든 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 인스턴스를 만들고
      • 자바스크립트 파일의 위치를 전달한다는 의미이다.
// 스크립트처럼 script.js를 로딩
let worker = new Worker("script.js");

// 모듈처럼 module.js를 로딩
let worker = new Worker("module.js", { type: "module"});
  • 워커 스크립트는 웹 페이지와 같은 출처에서만 로딩
    • but 워커 모듈은 그러한 제한이 없다.
      • 워커 모듈도 기본적으로 동일한 제약을 가지지만,
        • 접근을 허용하는 적절한 Cross-Origin Resource Sharing(CORS) 헤더를 가진 파일은 로드할 수 있다.
  • 스크립트에서는 워커에 추가적인 스크립트를 로딩하기 위해
    • self.importScripts() 메서드를 사용할 수 있지만
    • But 워커 모듈에서는 self.importScripts()가 항상 실패한다.

13.8.2 브라우저 모듈 명시자 결의안

  • 모듈 명시자로 (문자열 "./example.js") 상대 경로 사용 중
  • 사용 가능한 경로
    • 루트 /
    • 현재 디렉토리 ./
    • 부모 디렉토리 ../
    • URL 형식
<script type="module">
// 유효하지 않음 /, ./, ../ 로 시작해야 함
import { first } from "example.js";
    
import { second } from "example/index.js";
</script>
  • 이 처럼 정상적으로 보이는 일부 모듈 명시자도 브라우저에서 유효하지 않으며, 에러를 발생 시킨다.

13.9 요약

​~~~ 마무으리~~~~