확장 및 내보내기

익스텐션의 목적

extent는 클로저 컴파일러에 고급 컴파일 중에 이름을 변경하면 안 되는 기호의 이름을 알려주는 선언입니다. 기호는 컴파일 외부의 코드(예: 네이티브 코드 또는 서드 파티 라이브러리)에서 정의하는 경우가 가장 많기 때문에 extern이라고 합니다. 이런 이유로, extern에는 유형 주석도 있는 경우가 많으므로 클로저 컴파일러에서 이러한 기호의 사용을 유형 확인할 수 있습니다.

일반적으로 extextend는 컴파일된 코드 조각의 구현자와 소비자 간의 API 계약으로 생각하는 것이 가장 좋습니다. extern은 구현자가 제공할 것을 약속하고 소비자가 사용에 의존할 수 있는 사항을 정의합니다. 양측에 계약서 사본이 필요합니다.

별첨은 다른 언어의 헤더 파일과 유사합니다.

Externs 구문

extern은 클로저 컴파일러로 주석 처리된 일반 자바스크립트와 매우 유사한 파일입니다. 주요 차이점은 콘텐츠가 컴파일된 출력의 일부로 인쇄되지 않으므로 이름 및 유형만 의미가 있는 값이 없다는 것입니다.

다음은 간단한 라이브러리의 extern 파일입니다.

// The `@externs` annotation is the best way to indicate a file contains externs.

/**
 * @fileoverview Public API of my_math.js.
 * @externs
 */

// Externs often declare global namespaces.

const myMath = {};

// Externs can declare functions, most importantly their names.

/**
 * @param {number} x
 * @param {number} y
 * @return {!myMath.DivResult}
 */
myMath.div = function(x, y) {};  // Note the empty body.

// Externs can contain type declarations, such as classes and interfaces.

/** The result of an integer division. */
myMath.DivResult = class {

  // Constructors are special; member fields can be declared in their bodies.

  constructor() {
    /** @type {number} */
    this.quotient;
    /** @type {number} */
    this.remainder;
  }

  // Methods can be declared as usual; their bodies are meaningless though.

  /** @return {!Array<number>} */
  toPair() {}

};

// Fields and methods can also be declared using prototype notation.

/**
 * @override
 * @param {number=} radix
 */
myMath.DivResult.prototype.toString = function(radix) {};
    

--externs 플래그

일반적으로 @externs 주석은 파일에 extern이 포함되어 있음을 컴파일러에 알리는 가장 좋은 방법입니다. 이러한 파일은 --js 명령줄 플래그를 사용하여 일반 소스 파일로 포함될 수 있습니다.

하지만 외부 파일을 지정하는 또 다른 이전 방법이 있습니다. --externs 명령줄 플래그는 extern 파일을 명시적으로 전달하는 데 사용할 수 있습니다. 이 방법은 권장되지 않습니다.

외부 자료 사용

위의 extern은 다음과 같이 사용할 수 있습니다.

/**
 * @fileoverview Do some math.
 */

/**
 * @param {number} x
 * @param {number} y
 * @return {number}
 */
export function greatestCommonDivisor(x, y) {
  while (y != 0) {
    const temp = y;
    // `myMath` is a global, it and `myMath.div` are never renamed.
    const result = myMath.div(x, y);
    // `remainder` is also never renamed on instances of `DivResult`.
    y = result.remainder;
    x = temp;
  }
  return x;
}
    

Closure Compiler Service API로 확장을 포함하는 방법

Closure Compiler 애플리케이션과 Closure Compiler 서비스 API 모두 extern 선언이 허용됩니다. 하지만 클로저 컴파일러 서비스 UI는 extern 파일을 지정하기 위한 인터페이스 요소를 제공하지 않습니다.

extern 선언을 클로저 컴파일러 서비스에 전송하는 방법에는 세 가지가 있습니다.

  • @externs 주석이 포함된 파일을 소스 파일로 전달합니다.
  • js_externs 매개변수의 클로저 컴파일러 서비스에 자바스크립트를 전달합니다.
  • 자바스크립트 파일의 URL을 externs_url 매개변수의 클로저 컴파일러 서비스에 전달합니다.

js_externs 사용과 externs_url 사용의 유일한 차이점은 클로저 컴파일러 서비스에 자바스크립트가 통신하는 방식입니다.

수출 목적

내보내기는 컴파일 후 기호에 일관성 있는 이름을 지정하는 또 다른 메커니즘입니다. 외부 자료보다 덜 유용하며 종종 혼동을 야기함 단순한 사례를 제외한 모든 경우에 사용하지 않는 것이 좋습니다.

내보내기는 클로저 컴파일러가 문자열 리터럴을 수정하지 않는다는 사실에 의존합니다. 리터럴을 사용하여 이름이 지정된 속성에 객체를 할당하면 컴파일 후에도 해당 속성 이름을 통해 객체를 사용할 수 있습니다.

다음은 간단한 예입니다.

/**
 * @fileoverview Do some math.
 */

// Note that the concept of module exports is totally unrelated.

/** @return {number} */
export function myFunction() {
  return 5;
}

// This assignment ensures `myFunctionAlias` will be a global alias exposing `myFunction`,
// even after compilation.

window['myFunctionAlias'] = myFunction;
    

클로저 라이브러리를 사용하는 경우 goog.exportSymbolgoog.exportProperty 함수를 사용하여 내보내기를 선언할 수도 있습니다.

이러한 함수의 클로저 라이브러리 문서에서 자세한 내용을 확인할 수 있습니다. 그러나 특수 컴파일러를 지원하며 컴파일된 출력에서 완전히 변환된다는 점에 유의하세요.

내보내기 관련 문제

내보내기는 소비자가 참조할 수 있도록 노출된 별칭만 생성한다는 점에서 외부와 다릅니다. 컴파일된 코드 내에서 내보낸 기호의 이름은 계속 변경됩니다. 이러한 이유로 내보낸 기호는 상수여야 합니다. 코드에서 이 기호를 재할당하면 노출된 별칭이 잘못된 것을 가리키기 때문입니다.

이름 변경의 이러한 미묘한 차이는 내보낸 인스턴스 속성과 관련하여 특히 복잡합니다.

이론적으로는 긴 이름을 코드 내의 더 작은 이름으로 변경할 수 있으므로 내보내기를 extern에 비해 더 작은 코드 크기로 허용할 수 있습니다. 실제로 이러한 개선사항은 미미한 경우가 많으며 내보내기로 인해 발생하는 혼동을 정당화하지 않습니다.

또한 내보내기는 extern의 방식으로 소비자가 따를 수 있는 API를 제공하지 않습니다. 내보내기와 달리 extern은 노출하려는 기호와 유형을 문서화하고 사용 정보를 추가할 수 있는 공간을 제공합니다. 또한 소비자가 클로저 컴파일러를 사용하는 경우에도 컴파일할 외부 확장 프로그램이 필요합니다.