Sunteți pe pagina 1din 49

16.

Module #
16.1. Prezentare generală

16.1.1. Exporturi multiple denumite

16.1.2. Export implicit unic

16.1.3. Navigatoare: scripturi versus module

16.2. Module în JavaScript

16.2.1. Sisteme de module ECMAScript 5

16.2.2. Module ECMAScript 6

16.3. Bazele modulelor ES6

16.3.1. Exporturi denumite (mai multe pe modul)

16.3.2. Exporturi implicite (unul pe modul)

16.3.3. Importurile și exporturile trebuie să fie la cel mai înalt


nivel

16.3.4. Importurile sunt ridicate

16.3.5. Importurile sunt puncte de vedere de numai citire a


exporturilor

16.3.6. Asistență pentru dependențe ciclice

16.4. Import și export în detaliu

16.4.1. Stiluri de import


16.4.2. Stiluri exportatoare denumite: inline versus clauză

16.4.3. Re-exportatori

16.4.4. Toate stilurile de export

16.4.5. Având atât exporturi numite, cât și export implicit într-un


modul

16.5. API-ul de încărcare a modulului ECMAScript 6

16.5.1. Încărcătoare

16.5.2. Metoda încărcătorului: importarea modulelor

16.5.3. Mai multe metode de încărcare

16.5.4. Configurarea încărcării modulului

16.6. Utilizarea modulelor ES6 în browsere

16.6.1. Navigatoare: module asincrone versus scripturi sincrone

16.7. Detalii: importuri ca puncte de vedere asupra exporturilor

16.7.1. În CommonJS, importurile sunt copii ale valorilor


exportate

16.7.2. În ES6, importurile sunt vederi de citire în direct a


valorilor exportate

16.7.3. Implementarea vizualizărilor

16.7.4. Importurile ca vizualizări în spec

16.8. Obiective de proiectare pentru module ES6

16.8.1. Exporturile implicite sunt favorizate


16.8.2. Structura modulului static

16.8.3. Asistență atât pentru încărcare sincronă, cât și asincronă

16.8.4. Suport pentru dependențe ciclice între module

16.9. Întrebări frecvente: module

16.9.1. Pot folosi o variabilă pentru a specifica din ce modul


doresc să import?

16.9.2. Pot importa un modul condiționat sau la cerere?

16.9.3. Pot folosi variabile într-o importdeclarație?

16.9.4. Pot folosi distrugerea într-o importdeclarație?

16.9.5. Exporturile numite sunt necesare? De ce nu exportați


obiectele implicit?

16.9.6. Pot eval()codul modulului?

16.10. Avantajele modulelor ECMAScript 6

16.11. Citirea ulterioară

16.1 Prezentare generală


JavaScript are module de mult timp. Cu toate acestea, au fost puse în aplicare prin intermediul
bibliotecilor, nu construite în limbă. ES6 este prima dată când JavaScript are module integrate.

Modulele ES6 sunt stocate în fișiere. Există exact un modul pe fișier și un fișier per modul.
Aveți două moduri de a exporta lucrurile dintr-un modul. Aceste două moduri pot fi
amestecate , dar de obicei este mai bine să le utilizați separat.

16.1.1 Exporturi multiple numite


Exista mai multe exporturi numite :

//------ lib.js ------

export const sqrt = Math.sqrt;

export function square(x) {

return x * x;

export function diag(x, y) {

return sqrt(square(x) + square(y));

//------ main.js ------

import { square, diag } from 'lib';

console.log(square(11)); // 121

console.log(diag(4, 3)); // 5

Puteți importa, de asemenea, modulul complet:

//------ main.js ------

import * as lib from 'lib';

console.log(lib.square(11)); // 121

console.log(lib.diag(4, 3)); // 5

16.1.2 Export implicit unic

Poate exista un singur export implicit . De exemplu, o funcție:

//------ myFunc.js ------


export default function () { ··· } // no semicolon!

//------ main1.js ------

import myFunc from 'myFunc';

myFunc();

Sau o clasă:

//------ MyClass.js ------

export default class { ··· } // no semicolon!

//------ main2.js ------

import MyClass from 'MyClass';

const inst = new MyClass();

Rețineți că nu există nicio virgulă la sfârșit dacă exportați implicit o funcție sau o clasă (care sunt
declarații anonime).

16.1.3 Navigatoare: scripturi versus module


Scripturi module

Element HTML <script><script type="module">

Mod automat non-strictă strict

Variabilele de nivel superior sunt global local la modul

Valoarea thisnivelului superior windowundefined

executat sincronă asincronă

Importuri importdeclarative ( declarație) Nu da

Importuri programatice (API-ul bazat pe promisiuni) da da

Extensia de fișier .js .js


16.2 Module în JavaScript
Chiar dacă JavaScript nu a avut niciodată module integrate, comunitatea a convergent într-un stil
simplu de module, care este susținut de bibliotecile din ES5 și anterioare. Acest stil a fost adoptat
și de ES6:

Fiecare modul este o bucată de cod care se execută odată ce este încărcat.

În codul respectiv, pot exista declarații (declarații variabile, declarații funcționale etc.).

În mod implicit, aceste declarații rămân locale pentru modul.

Puteți marca unele dintre ele ca exporturi, apoi alte module le pot importa.

Un modul poate importa lucrurile din alte module. Se referă la acele module prin intermediul
specificatorilor modulului , șiruri care sunt:

Căi relative ( '../model/user'): aceste căi sunt interpretate relativ în funcție de locația modulului
importator. Extensia de fișier .jspoate fi de obicei omisă.

Căi absolute ( '/lib/js/helpers'): indicați direct către fișierul modulului care trebuie importat.

Nume ( 'util'): la ce module se referă numele trebuie să fie configurat.

Modulele sunt singletone. Chiar dacă un modul este importat de mai multe ori, există o singură
„instanță” a acestuia.

Această abordare a modulelor evită variabile globale, singurele lucruri care sunt globale sunt
specificatorii modulelor.

16.2.1 ECMAScript 5 sisteme de module


Este impresionant cât de bine funcționează sistemele de module ES5 fără sprijin explicit din
partea limbii. Cele mai importante două (și, din păcate, incompatibile) standarde sunt:

Module CommonJS: Implementarea dominantă a acestui standard este în Node.js (modulele


Node.js au câteva caracteristici care depășesc CommonJS). caracteristici:

Sintaxa compactă

Proiectat pentru încărcare sincronă și servere


Definirea modulului asincron (AMD): cea mai populară implementare a acestui standard este
RequJS . caracteristici:

Sintaxa puțin mai complicată, care permite AMD să funcționeze fără evalu () (sau o etapă de
compilare)

Proiectat pentru încărcare asincronă și browsere

Cele de mai sus nu sunt decât o explicație simplificată a modulelor ES5. Dacă doriți mai multe
materiale aprofundate, aruncați o privire la „ Scrierea JavaScript Modular cu AMD, CommonJS și
ES Harmony ” de Addy Osmani.

16.2.2 ECMAScript 6 module


Obiectivul pentru modulele ECMAScript 6 a fost crearea unui format de care utilizatorii
CommonJS și AMD sunt mulțumiți de:

În mod similar CommonJS, acestea au o sintaxă compactă, o preferință pentru exporturi unice și
suport pentru dependențele ciclice.

În mod similar AMD, au suport direct pentru încărcarea asincronă și încărcarea modulului
configurabil.

Fiind încorporat în limbaj, permite modulelor ES6 să depășească CommonJS și AMD (detaliile
sunt explicate mai târziu):

Sintaxa lor este chiar mai compactă decât cea a CommonJS.

Structura lor poate fi analizată static (pentru verificare statică, optimizare etc.).

Suportul lor pentru dependențele ciclice este mai bun decât cel al CommonJS.

Standardul modulului ES6 are două părți:

Sintaxa declarativă (pentru import și export)

API programatică de încărcare: pentru a configura modul în care sunt încărcate modulele și
pentru a încărca condițional modulele

16.3 Bazele modulelor ES6


Există două tipuri de exporturi: exporturi numite (mai multe pe modul) și exporturi implicite
(unul pe modul). Așa cum s-a explicat mai târziu , este posibilă utilizarea ambelor în același timp,
dar de obicei cel mai bine pentru a le menține separate.

16.3.1 Exporturi denumite (mai multe pe module)


Un modul poate exporta mai multe lucruri prin prefixarea declarațiilor sale cu cuvântul cheie
export. Aceste exporturi se disting prin numele lor și se numesc exporturi numite .

//------ lib.js ------

export const sqrt = Math.sqrt;

export function square(x) {

return x * x;

export function diag(x, y) {

return sqrt(square(x) + square(y));

//------ main.js ------

import { square, diag } from 'lib';

console.log(square(11)); // 121

console.log(diag(4, 3)); // 5

Există și alte modalități de a specifica exporturile numite (care sunt explicate mai târziu), dar mi
se pare unul destul de convenabil: pur și simplu scrieți-vă codul ca și cum nu ar exista lume
exterioară, apoi etichetați tot ce doriți să exportați cu un cuvânt cheie

Dacă doriți, puteți importa, de asemenea, întregul modul și consultați exporturile numite prin
notarea proprietății:
//------ main.js ------

import * as lib from 'lib';

console.log(lib.square(11)); // 121

console.log(lib.diag(4, 3)); // 5

Același cod în sintaxa CommonJS: Pentru un timp, am încercat mai multe strategii inteligente
pentru a fi mai puțin redundant cu exporturile modulului meu în Node.js. Acum prefer următorul
stil simplu, dar ușor verbos, care amintește de modelul modulului revelator :

//------ lib.js ------

var sqrt = Math.sqrt;

function square(x) {

return x * x;

function diag(x, y) {

return sqrt(square(x) + square(y));

module.exports = {

sqrt: sqrt,

square: square,

diag: diag,

};

//------ main.js ------

var square = require('lib').square;

var diag = require('lib').diag;

console.log(square(11)); // 121
console.log(diag(4, 3)); // 5

16.3.2 Exporturi implicite (unul pe modul)

Modulele care exportă doar valori unice sunt foarte populare în comunitatea Node.js. Dar sunt
de asemenea frecvente în dezvoltarea de frontenduri, unde aveți deseori clase pentru modele și
componente, cu o clasă pe modul. Un modul ES6 poate alege un export implicit , valoarea
principală exportată. Exporturile implicite sunt foarte ușor de importat.

Următorul modul ECMAScript 6 „este” o singură funcție:

//------ myFunc.js ------

export default function () {} // no semicolon!

//------ main1.js ------

import myFunc from 'myFunc';

myFunc();

Un modul ECMAScript 6 al cărui export implicit este o clasă arată după cum urmează:

//------ MyClass.js ------

export default class {} // no semicolon!

//------ main2.js ------

import MyClass from 'MyClass';

const inst = new MyClass();

Există două stiluri de export implicit:

Declarații de etichetare

Valorile implicite exportate direct


16.3.2.1 Stilul implicit de export 1: declarații de etichetare
Puteți prefixa orice declarație de funcție (sau declarație de funcție a generatorului) sau
declarație de clasă cu cuvintele cheie export defaultpentru a o face exportul implicit:

export default function foo() {} // no semicolon!

export default class Bar {} // no semicolon!

De asemenea, puteți omite numele în acest caz. Aceasta face ca exporturile implicite să fie
singurul loc în care JavaScript are declarații de funcții anonime și declarații de clasă anonime:

export default function () {} // no semicolon!

export default class {} // no semicolon!

16.3.2.1.1 De ce declarațiile de funcții anonime și nu expresiile


de funcții anonime?
Când te uiți la cele două rânduri de cod anterioare, te aștepți ca operandurile export defaultsă
fie expresii. Ele sunt doar declarații din motive de coerență: operanzii pot fi numiți declarații,
interpretând versiunile lor anonime ca expresii ar fi confuze (chiar mai mult decât introducerea
de noi tipuri de declarații).

Dacă doriți ca operanzele să fie interpretate ca expresii, trebuie să utilizați paranteze:

export default (function () {});

export default (class {});

16.3.2.2 Stilul de export implicit 2: valorile implicit-exportate


direct
Valorile sunt produse prin expresii:

export default 'abc';


export default foo();

export default /^xyz$/;

export default 5 * 7;

export default { no: false, yes: true };

Fiecare dintre aceste exporturi implicite are următoarea structură.

export default «expression»;

Aceasta este echivalentă cu:

const __default__ = «expression»;

export { __default__ as default }; // (A)

Declarația din linia A este o clauză de export (care este explicată într- o secțiune ulterioară ).

16.3.2.2.1 De ce două stiluri de export implicite?


Cel de-al doilea stil de export implicit a fost introdus deoarece declarațiile variabile nu pot fi
transformate în mod semnificativ în exporturi implicite dacă declară mai multe variabile:

export default const foo = 1, bar = 2, baz = 3; // not legal JavaScript!

Care dintre cele trei variabile foo, barși bazar fi exportul implicit?

16.3.3 Importurile și exporturile trebuie să se afle la cel mai înalt


nivel
După cum am explicat mai detaliat mai târziu, structura modulelor ES6 este statică , nu puteți
importa sau exporta condițional lucrurile. Aceasta aduce o varietate de beneficii.

Această restricție este aplicată sintactic, permițând doar importurile și exporturile la nivelul
superior al unui modul:

if (Math.random()) {

import 'foo'; // SyntaxError

// You can’t even nest `import` and `export`

// inside a simple block:

import 'foo'; // SyntaxError

16.3.4 Importurile sunt ridicate


Importurile de module sunt ridicate (mutat intern la începutul domeniului de aplicare actual).
Prin urmare, nu contează unde le menționați într-un modul și următorul cod funcționează fără
probleme:

foo();

import { foo } from 'my_module';

16.3.5 Importurile sunt puncte de vedere numai de citire a


exporturilor
Importurile unui modul ES6 sunt vederi de numai citire asupra entităților exportate. Aceasta
înseamnă că conexiunile la variabilele declarate în interiorul corpurilor modulului rămân actuale,
așa cum se arată în codul următor.

//------ lib.js ------

export let counter = 3;


export function incCounter() {

counter++;

//------ main.js ------

import { counter, incCounter } from './lib';

// The imported value `counter` is live

console.log(counter); // 3

incCounter();

console.log(counter); // 4

Cum funcționează asta sub capotă este explicat într-o secțiune ulterioară .

Importurile sub formă de vizualizări prezintă următoarele avantaje:

Ele activează dependențe ciclice, chiar și pentru importuri necalificate (așa cum este explicat în
secțiunea următoare).

Importurile calificate și necalificate funcționează la fel (sunt ambele indicații).

Puteți împărți codul în mai multe module și va continua să funcționeze (atât timp cât nu încercați
să modificați valorile importurilor).

16.3.6 Suport pentru dependențele ciclice


Două module A și B sunt dependente ciclic unele de altele, dacă ambele A (eventual indirect /
tranzitiv) importă B și B importă A. Dacă este posibil, trebuie evitate dependențele ciclice,
acestea duc la A și B strâns cuplate - acestea pot fi doar folosit și evoluat împreună.

Atunci de ce să susținem dependențele ciclice? Ocazional, nu poți să te ocolești, motiv pentru


care sprijinul pentru acestea este o caracteristică importantă. O secțiune ulterioară conține mai
multe informații.
Să vedem cum gestionează CommonJS și ECMAScript 6 dependențe ciclice.

16.3.6.1 Dependențe ciclice în CommonJS


Următorul cod CommonJS gestionează corect două module ași bdepinde ciclic unul de altul.

//------ a.js ------

var b = require('b');

function foo() {

b.bar();

exports.foo = foo;

//------ b.js ------

var a = require('a'); // (i)

function bar() {

if (Math.random()) {

a.foo(); // (ii)

exports.bar = bar;

Dacă modulul aeste importat mai întâi atunci, în linia i, modulul bprimește aobiectul exporturilor
înainte ca exporturile să fie adăugate la acesta. Prin urmare, bnu se poate accesa a.foola nivelul
său superior, dar acea proprietate există odată ce execuția lui aeste terminată. Dacă bar()este
apelat după aceea, funcționarea apelului din linia II funcționează.
De regulă, rețineți că, cu dependențe ciclice, nu puteți accesa importurile în corpul modulului.
Acest lucru este inerent fenomenului și nu se schimbă cu modulele ECMAScript 6.

Limitările abordării CommonJS sunt:

Exporturile de o singură valoare în stil Node.js nu funcționează. Acolo, exportați valori unice în
loc de obiecte:

module.exports = function () { ··· };

Dacă modul a afăcut acest lucru, atunci bvariabila modulului anu ar fi actualizată odată ce s-a
făcut atribuirea. Ar continua să se refere la obiectul inițial de export.

Nu puteți utiliza exporturile numite direct. Adică, modulul bnu poate importa fooastfel:

var foo = require('a').foo;

foopur și simplu ar fi undefined. Cu alte cuvinte, nu aveți de ales decât să faceți referire la foovia
a.foo.

Aceste limitări înseamnă că atât exportatorul cât și importatorii trebuie să fie conștienți de
dependențele ciclice și să le sprijine în mod explicit.

16.3.6.2 Dependențe ciclice în ECMAScript 6


Modulele ES6 acceptă automat dependențele ciclice. Adică, acestea nu au cele două limitări ale
modulelor CommonJS menționate în secțiunea anterioară: exporturile implicite funcționează, la
fel ca și importurile numite necalificate (liniile i și iii din exemplul următor). Prin urmare, puteți
implementa module care depind ciclic unele de altele, după cum urmează.

//------ a.js ------

import {bar} from 'b'; // (i)

export function foo() {


bar(); // (ii)

//------ b.js ------

import {foo} from 'a'; // (iii)

export function bar() {

if (Math.random()) {

foo(); // (iv)

Acest cod funcționează, deoarece, așa cum este explicat în secțiunea precedentă, importurile
reprezintă opinii asupra exporturilor. Asta înseamnă că chiar și importurile necalificate (cum ar fi
barlinia ii și foolinia iv) sunt indicații care se referă la datele originale. Astfel, în fața
dependențelor ciclice, nu contează dacă accesați un export numit printr-un import necalificat
sau prin modulul său: Există o indirecție în ambele cazuri și funcționează întotdeauna.

16.4 Importul și exportul în detaliu


16.4.1 Importarea stilurilor
ECMAScript 6 oferă mai multe stiluri de importare 2 :

Import implicit:

import localName from 'src/my_lib';

Importarea spațiului de nume: importă modulul ca obiect (cu o proprietate pe export numit).

import * as my_lib from 'src/my_lib';

Importuri denumite:

import { name1, name2 } from 'src/my_lib';


Puteți redenumi importurile numite:

// Renaming: import `name1` as `localName1`

import { name1 as localName1, name2 } from 'src/my_lib';

// Renaming: import the default export as `foo`

import { default as foo } from 'src/my_lib';

Import gol: încarcă doar modulul, nu importă nimic. Primul astfel de import într-un program
execută corpul modulului.

import 'src/my_lib';

Există doar două moduri de a combina aceste stiluri și ordinea în care apar este fixată; exportul
implicit vine întotdeauna pe primul loc.

Combinarea unui import implicit cu un import de spațiu de nume:

import theDefault, * as my_lib from 'src/my_lib';

Combinarea unui import implicit cu importuri numite

import theDefault, { name1, name2 } from 'src/my_lib';

16.4.2 Stiluri de export numite: inline versus clauza

Există două moduri în care puteți exporta lucrurile numite în module.

Pe de o parte, puteți marca declarațiile cu cuvântul cheie export.

export var myVar1 = ···;

export let myVar2 = ···;

export const MY_CONST = ···;


export function myFunc() {

···

export function* myGeneratorFunc() {

···

export class MyClass {

···

Pe de altă parte, puteți enumera tot ceea ce doriți să exportați la sfârșitul modulului (care este
similar în stil cu modelul modulului revelator).

const MY_CONST = ···;

function myFunc() {

···

export { MY_CONST, myFunc };

De asemenea, puteți exporta lucruri sub diferite denumiri:

export { MY_CONST as FOO, myFunc };

16.4.3 Reexportarea
Reexport înseamnă adăugarea exporturilor unui alt modul la cele ale modulului actual. Puteți
adăuga toate exporturile celuilalt modul:

export * from 'src/other_module';


Exporturile implicite sunt ignorate 3 până la export *.

Sau puteți fi mai selectiv (opțional în timp ce redenumiți):

export { foo, bar } from 'src/other_module';

// Renaming: export other_module’s foo as myFoo

export { foo as myFoo, bar } from 'src/other_module';

16.4.3.1 Realizarea unei reexportări a exportului implicit


Următoarea declarație face ca exportul implicit al altui modul să foofie exportul implicit al
modulului curent:

export { default } from 'foo';

Următoarea declarație face ca exportul numit myFuncal modulului să foofie exportul implicit al
modulului curent:

export { myFunc as default } from 'foo';

16.4.4 Toate stilurile de export

ECMAScript 6 oferă mai multe stiluri de export 4 :

Re-exportatori:

Reexportează totul (cu excepția exportului implicit):

export * from 'src/other_module';

Reexportarea printr-o clauză:

export { foo as myFoo, bar } from 'src/other_module';


export { default } from 'src/other_module';

export { default as foo } from 'src/other_module';

export { foo as default } from 'src/other_module';

Numit exportator printr-o clauză:

export { MY_CONST as FOO, myFunc };

export { foo as default };

Exporturi numite inline:

Declarații variabile:

export var foo;

export let foo;

export const foo;

Declarații de funcție:

export function myFunc() {}

export function* myGenFunc() {}

Declarații de clasă:

export class MyClass {}

Export implicit:

Declarații de funcție (pot fi anonime aici):

export default function myFunc() {}

export default function () {}

export default function* myGenFunc() {}

export default function* () {}

Declarații de clasă (pot fi anonime aici):

export default class MyClass {}


export default class {}

Expresii: valori de export. Notează punctele și virgulele de la sfârșit.

export default foo;

export default 'Hello world!';

export default 3 * 7;

export default (function () {});

16.4.5 Au ambele exporturi numite și un export implicit într-un modul

Următorul model este surprinzător de comun în JavaScript: o bibliotecă este o singură funcție,
dar servicii suplimentare sunt furnizate prin proprietățile acelei funcții. Exemple includ jQuery și
Underscore.js. Următoarea este o schiță a Underscore ca modul CommonJS:

//------ underscore.js ------

var _ = function (obj) {

···

};

var each = _.each = _.forEach =

function (obj, iterator, context) {

···

};

module.exports = _;

//------ main.js ------

var _ = require('underscore');

var each = _.each;

···

Cu ochelari ES6, funcția _este exportul implicit, în timp ce eachși forEachsunt denumite
exporturi. După cum se dovedește, puteți avea, de fapt, exporturi numite și export implicit în
același timp. Ca exemplu, modulul CommonJS anterior, rescris ca modul ES6, arată astfel:

//------ underscore.js ------

export default function (obj) {

···

export function each(obj, iterator, context) {

···

export { each as forEach };

//------ main.js ------

import _, { each } from 'underscore';

···

Rețineți că versiunea CommonJS și versiunea ECMAScript 6 sunt doar aproximativ similare.


Acesta din urmă are o structură plană, în timp ce primul este cuibărit.

16.4.5.1 Recomandare: evitați să amestecați exporturile implicite și exporturile numite

În general, recomand să păstrați separat cele două tipuri de export: pe modul, fie aveți doar un
export implicit sau au doar exporturi numite.

Totuși, aceasta nu este o recomandare foarte puternică; uneori poate avea sens să amesteci cele
două tipuri. Un exemplu este un modul care exportă implicit o entitate. Pentru testele unitare,
puteți pune la dispoziție, în plus, unele dintre cele interne prin exporturi numite.

16.4.5.2 Exportul implicit este doar un alt exportator numit


Exportul implicit este de fapt doar un export numit cu numele special default. Adică,
următoarele două afirmații sunt echivalente:

import { default as foo } from 'lib';

import foo from 'lib';

În mod similar, următoarele două module au același export implicit:

//------ module1.js ------

export default function foo() {} // function declaration!

//------ module2.js ------

function foo() {}

export { foo as default };

16.4.5.3default : OK ca nume de export, dar nu ca nume variabil


Nu puteți utiliza cuvinte rezervate (cum ar fi defaultși new) ca nume variabile, dar le puteți utiliza
ca nume pentru export (le puteți utiliza și ca nume de proprietate în ECMAScript 5). Dacă doriți
să importați direct astfel de exporturi numite, trebuie să le redenumiți pentru nume de variabile
adecvate.

Asta înseamnă că defaultpoate apărea doar pe partea stângă a unui import de redenumire:

import { default as foo } from 'some_module';

Și poate apărea doar în partea dreaptă a unui export de redenumire:

export { foo as default };

În reexport, ambele părți assunt denumiri de export:


export { myFunc as default } from 'foo';

export { default as otherFunc } from 'foo';

// The following two statements are equivalent:

export { default } from 'foo';

export { default as default } from 'foo';

16.5 ECMAScript 6 Modul încărcător API


Pe lângă sintaxa declarativă pentru lucrul cu module, există și o API programatică. Vă permite să:

Lucrează programatic cu module

Configurați încărcarea modulului

API-ul încărcătorului modulului nu face parte din standardul ES6

Acesta va fi specificat într-un document separat, „JavaScript Loader Standard”, care va fi evoluat
mai dinamic decât specificația de limbă. Depozitul pentru acel document prevede:

[Standardul de încărcare JavaScript] consolidează lucrul la semantica de încărcare a modulului


ECMAScript cu punctele de integrare ale browserelor Web, precum și Node.js.

API-ul încărcătorului modulului este în lucru

După cum puteți vedea în depozitul Standardului de încărcare JavaScript , API-ul de încărcare a
modulului este încă în curs de desfășurare. Tot ce ai citit despre asta în această carte este
tentativ. Pentru a avea impresia cum ar putea arăta API-ul, puteți arunca o privire la polifileta de
încărcare a modulelor ES6 de pe GitHub.

16.5.1 Încărcătoare
Încărcătoarele se ocupă cu specificațiile modulului de rezolvare (ID-urile șirului de la sfârșitul
import-from), modulele de încărcare, etc. Constructorul lor este Reflect.Loader. Fiecare
platformă păstrează o instanță implicită în variabila globală System( încărcătorul de sistem ), care
implementează stilul său specific de încărcare a modulelor.

16.5.2 Metoda încărcătorului: importarea modulelor


Puteți importa programatic un modul, printr-o API bazată pe Promise :

System.import('some_module')

.then(some_module => {

// Use some_module

})

.catch(error => {

···

});

System.import() vă permite să:

Utilizați module în interiorul <script>elementelor (unde sintaxa modulului nu este acceptată,


consultați secțiunea despre module versus scripturi pentru detalii).

Modulele de încărcare sunt condiționate.

System.import()preia un singur modul, puteți utiliza Promise.all()pentru a importa mai multe


module:

Promise.all(

['module1', 'module2', 'module3']

.map(x => System.import(x)))

.then(([module1, module2, module3]) => {

// Use module1, module2, module3


});

16.5.3 Mai multe metode de încărcare


Încărcătoarele au mai multe metode. Trei importante sunt:

System.module(source, options?)

evaluează codul JavaScript într- sourceun modul (care este livrat asincron prin intermediul unei
promisiuni).

System.set(name, module)

este pentru înregistrarea unui modul (de exemplu, unul pe care l-ați creat prin intermediul
System.module()).

System.define(name, source, options?)

ambele evaluează codul modulului în sourceși înregistrează rezultatul.

16.5.4 Configurarea încărcării modulului


API-ul încărcătorului modulului va avea diverse cârlige pentru configurarea procesului de
încărcare. Cazurile de utilizare includ:

Module Lint la import (de exemplu, prin JSLint sau JSHint).

Traduceți automat modulele la import (ar putea conține codul CoffeeScript sau TypeScript).

Utilizați module vechi (AMD, Node.js).

Încărcarea modulului configurabil este o zonă în care Node.js și CommonJS sunt limitate.

16.6 Utilizarea modulelor ES6 în browserele

Să analizăm modul în care modulele ES6 sunt acceptate în browsere.

Asistența pentru modulele ES6 în browsere este în desfășurare

La fel ca în cazul încărcării modulelor, încă se lucrează la alte aspecte ale suportului pentru
module din browsere. Tot ce ai citit aici se poate schimba.

16.6.1 Navigatoare: module asincrone versus scripturi sincrone

În browsere, există două tipuri diferite de entități: scripturi și module. Au sintaxa ușor diferită și
funcționează diferit.

Acesta este un rezumat al diferențelor, detaliile sunt explicate mai târziu:

Scripturi module

Element HTML <script><script type="module">

Mod automat non-strictă strict

Variabilele de nivel superior sunt global local la modul

Valoarea thisnivelului superior windowundefined

executat sincronă asincronă

Importuri importdeclarative ( declarație) Nu da

Importuri programatice (API-ul bazat pe promisiuni) da da

Extensia de fișier .js .js

16.6.1.1 Scripturi
Scripturile sunt modul tradițional al browserului de a încorpora JavaScript și de a face referire la
fișiere JavaScript externe. Scripturile au un tip de suport de internet care este utilizat ca:

Tipul de conținut al fișierelor JavaScript livrate prin intermediul unui server web.

Valoarea atributului typede <script>elemente. Rețineți că, pentru HTML5, recomandarea este să
omiteți typeatributul în <script>elemente, dacă acestea conțin sau se referă la JavaScript.

Următoarele sunt cele mai importante valori:


text/javascript: este o valoare moștenită și utilizată ca implicită dacă omiteți typeatributul dintr-o
etichetă de script. Este cea mai sigură alegere pentru Internet Explorer 8 și versiuni anterioare.

application/javascript: este recomandat pentru browserele actuale.

Scripturile sunt în mod normal încărcate sau executate sincron. Firul JavaScript se oprește până
când codul a fost încărcat sau executat.

16.6.1.2 Module
Pentru a fi în concordanță cu obișnuita semantică de execuție-finalizare JavaScript, corpul unui
modul trebuie executat fără întrerupere. Aceasta lasă două opțiuni pentru importul modulelor:

Încărcați modulele în mod sincron, în timp ce corpul este executat. Asta face Node.js.

Încărcați toate modulele în mod asincron, înainte de executarea corpului. Așa se gestionează
modulele AMD. Este cea mai bună opțiune pentru browsere, deoarece modulele sunt încărcate
pe internet și execuția nu trebuie să se întrerupă în timp ce sunt. Ca beneficiu suplimentar,
această abordare permite încărcarea simultană a mai multor module.

ECMAScript 6 vă oferă cele mai bune din ambele lumi: Sintaxa sincronă a Node.js plus încărcarea
asincronă a AMD. Pentru a face posibilele două, modulele ES6 sunt sintactic mai puțin flexibile
decât modulele Node.js: Importurile și exporturile trebuie să se întâmple la nivelul superior. Asta
înseamnă că nici ele nu pot fi condiționate. Această restricție permite unui încărcător de module
ES6 să analizeze static ce module sunt importate de un modul și să le încarce înainte de a-și
executa corpul.

Natura sincronă a scripturilor îi împiedică să devină module. Scripturile nu pot importa modulele
în mod declarativ (trebuie să folosiți API-ul de încărcare a modulelor programatice dacă doriți să
faceți acest lucru).

Modulele pot fi utilizate din browsere printr-o nouă variantă a <script>elementului care este
complet asincron:

<script type="module">
import $ from 'lib/jquery';

var x = 123;

// The current scope is not global

console.log('$' in window); // false

console.log('x' in window); // false

// `this` is undefined

console.log(this === undefined); // true

</script>

După cum puteți vedea, elementul are propriul său domeniu de aplicare și variabilele „din
interior” sunt locale pentru acel domeniu. Rețineți că codul modulului este implicit în modul
strict. Aceasta este o veste grozavă - nu mai mult 'use strict'.

Similar cu <script>elementele normale , <script type="module">poate fi folosit și pentru


încărcarea modulelor externe. De exemplu, următoarea etichetă pornește o aplicație web printr-
un mainmodul (numele atributului importeste invenția mea, încă nu este clar ce nume va fi
folosit).

<script type="module" import="impl/main"></script>

Avantajul <script>suportării modulelor în HTML printr-un tip personalizat este faptul că este ușor
de adus acel suport la motoarele mai vechi printr-o polifilare (o bibliotecă). În cele din urmă
poate exista sau nu un element dedicat pentru module (de exemplu <module>).

16.6.1.3 Modul sau script - o chestiune de context


Dacă un fișier este un modul sau un script este determinat doar de modul în care este importat
sau încărcat. Majoritatea modulelor au fie importuri, fie exporturi și pot fi astfel detectate. Dar
dacă un modul nu are nici atunci, acesta nu este diferențiat de un script. De exemplu:
var x = 123;

Semantica acestei bucăți de cod diferă în funcție de faptul dacă este interpretată ca un modul
sau ca un script:

Ca modul, variabila xeste creată în domeniul modulului.

Ca script, variabila xdevine o variabilă globală și o proprietate a obiectului global ( windowîn


browsere).

Un exemplu mai realist este un modul care instalează ceva, de exemplu, o polifilare în variabile
globale sau un ascultător de evenimente globale. Un astfel de modul nu importă și nici nu
exportă nimic și este activat printr-un import gol:

import './my_module';

Sursele acestei secțiuni

„ Module: Actualizare stare ”, diapozitive de David Herman.

„ Module vs Scripturi ”, un e-mail de David Herman.

16.7 Detalii: importurile ca puncte de vedere asupra exporturilor

Codul din această secțiune este disponibil pe GitHub .

Importurile funcționează diferit în CommonJS și ES6:

În CommonJS, importurile sunt copii ale valorilor exportate.

În ES6, importurile sunt vederi de citire în direct a valorilor exportate.

Următoarele secțiuni explică ce înseamnă asta.

16.7.1 În CommonJS, importurile sunt copii ale valorilor


exportate
Cu modulele CommonJS (Node.js), lucrurile funcționează în moduri relativ familiare.
Dacă importați o valoare într-o variabilă, valoarea este copiată de două ori: o dată când este
exportată (linia A) și o dată importată (linia B).

//------ lib.js ------

var counter = 3;

function incCounter() {

counter++;

module.exports = {

counter: counter, // (A)

incCounter: incCounter,

};

//------ main1.js ------

var counter = require('./lib').counter; // (B)

var incCounter = require('./lib').incCounter;

// The imported value is a (disconnected) copy of a copy

console.log(counter); // 3

incCounter();

console.log(counter); // 3

// The imported value can be changed

counter++;

console.log(counter); // 4
Dacă accesați valoarea prin intermediul obiectului de export, aceasta este încă copiată o dată, la
export:

//------ main2.js ------

var lib = require('./lib');

// The imported value is a (disconnected) copy

console.log(lib.counter); // 3

lib.incCounter();

console.log(lib.counter); // 3

// The imported value can be changed

lib.counter++;

console.log(lib.counter); // 4

16.7.2 În ES6, importurile sunt vederi de citire în direct a valorilor


exportate
Spre deosebire de CommonJS, importurile sunt opinii asupra valorilor exportate. Cu alte cuvinte,
fiecare import este o conexiune în direct la datele exportate. Importurile sunt numai de citire:

Importurile necalificate ( import x from 'foo') sunt constvariabile similare- declarate.

Proprietățile unui obiect modul foo( import * as foo from 'foo') sunt ca proprietățile unui obiect
înghețat .

Următorul cod demonstrează modul în care importurile sunt vizionări:

//------ lib.js ------

export let counter = 3;


export function incCounter() {

counter++;

//------ main1.js ------

import { counter, incCounter } from './lib';

// The imported value `counter` is live

console.log(counter); // 3

incCounter();

console.log(counter); // 4

// The imported value can’t be changed

counter++; // TypeError

Dacă importați obiectul modulului prin asterisc ( *), obțineți aceleași rezultate:

//------ main2.js ------

import * as lib from './lib';

// The imported value `counter` is live

console.log(lib.counter); // 3

lib.incCounter();

console.log(lib.counter); // 4

// The imported value can’t be changed


lib.counter++; // TypeError

Rețineți că, în timp ce nu puteți modifica valorile importurilor, puteți modifica obiectele la care
fac referire. De exemplu:

//------ lib.js ------

export let obj = {};

//------ main.js ------

import { obj } from './lib';

obj.prop = 123; // OK

obj = {}; // TypeError

16.7.2.1 De ce o nouă abordare a importului?


De ce să introducem un astfel de mecanism relativ complicat de import care se abate de la
practicile consacrate?

Dependențe ciclice: avantajul principal este că acceptă dependențe ciclice chiar și pentru
importuri necalificate.

Importurile calificate și necalificate funcționează la fel. În CommonJS, acestea nu: un import


calificat oferă acces direct la o proprietate a obiectului de export al modulului, un import
necalificat este o copie a acestuia.

Puteți împărți codul în mai multe module și va continua să funcționeze (atât timp cât nu încercați
să modificați valorile importurilor).

Pe partea flip, plierea modulului , combinarea mai multor module într-un singur modul devine,
de asemenea, mai simplă.

În experiența mea, importurile ES6 funcționează doar, rar trebuie să te gândești la ce se întâmplă
sub capotă.
16.7.3 Implementarea punctelor de vedere
Cum funcționează importurile ca vederi ale exporturilor sub capotă? Exporturile sunt gestionate
prin înregistrarea de export a structurii de date . Toate intrările la export (cu excepția celor
pentru reexport) au următoarele două nume:

Nume local: este numele sub care exportul este stocat în modul.

Numele exportului: este numele pe care trebuie să-l folosească modulele de import pentru a
accesa exportul.

După ce ați importat o entitate, acea entitate este accesată întotdeauna printr-un indicator care
are modulul celor două componente și numele local . Cu alte cuvinte, acel pointer se referă la o
legare (spațiul de stocare a unei variabile) în interiorul unui modul.

Să examinăm numele exporturilor și numele locale create de diverse tipuri de export. Următorul
tabel ( adaptat din spec. ES6 ) oferă o imagine de ansamblu, secțiunile ulterioare au mai multe
detalii.

Afirmație Numele local Numele exportului

export {v}; 'v' 'v'

export {v as x}; 'v' 'x'

export const v = 123; 'v' 'v'

export function f() {} 'f' 'f'

export default function f() {} 'f' 'default'

export default function () {} '*default*' 'default'

export default 123; '*default*' 'default'

16.7.3.1 Clauza de export


function foo() {}

export { foo };

Nume local: foo


Numele exportului: foo

function foo() {}

export { foo as bar };

Nume local: foo

Numele exportului: bar

16.7.3.2 Exporturi in linie

Acesta este un export inline:

export function foo() {}

Este echivalent cu următorul cod:

function foo() {}

export { foo };

Prin urmare, avem următoarele nume:

Nume local: foo

Numele exportului: foo

16.7.3.3 Exporturi implicite

Există două tipuri de exporturi implicite:

Exporturile implicite de declarații ridicabile ( declarații de funcții, declarații de funcții ale


generatorului) și declarații de clasă sunt similare cu exporturile in linie normale în cele
menționate sunt create și etichetate.

Toate celelalte exporturi implicite se referă la exportul rezultatelor expresiilor.

16.7.3.3.1 Expresii de export implicite


Următorul cod implicit exportă rezultatul expresiei 123:
export default 123;

Este echivalent cu:

const *default* = 123; // *not* legal JavaScript

export { *default* as default };

Dacă exportați în mod implicit o expresie, primiți:

Nume local: *default*

Numele exportului: default

Numele local a fost ales astfel încât să nu se ciocnească cu niciun alt nume local.

Rețineți că un export implicit duce la crearea unei legături. Dar, din cauza faptului că *default*nu
este un identificator legal, nu puteți accesa această legătură din interiorul modulului.

16.7.3.3.2 Declarații de ridicare și declarații de clasă exportatoare


implicite
Următorul cod implicit exportă o declarație de funcție:

export default function foo() {}

Este echivalent cu:

function foo() {}

export { foo as default };

Numele sunt:
Nume local: foo

Numele exportului: default

Asta înseamnă că puteți modifica valoarea exportului implicit din interiorul modulului, alocând o
altă valoare foo.

(Numai) pentru exporturile implicite, puteți omite și numele unei declarații de funcție:

export default function () {}

Aceasta este echivalentă cu:

function *default*() {} // *not* legal JavaScript

export { *default* as default };

Numele sunt:

Nume local: *default*

Numele exportului: default

Declarațiile de generare-exportator și declarațiile de clasă funcționează în mod similar cu


declarațiile de funcții implicite-exportatoare.

16.7.4 Importuri ca vizualizări în numărul


Această secțiune oferă indicatoare în specificațiile de limbă ECMAScript 2015 (ES6).

Gestionarea importurilor:

CreateImportBinding () creează legături locale pentru importuri.

GetBindingValue () este utilizat pentru a le accesa.


ModuleDeclarationInstantiation () stabilește mediul unui modul (comparați:
FunctionDeclarationInstantiation () , BlockDeclarationInstantiation () ).

Numele de export și numele locale create de diferitele tipuri de export sunt prezentate în tabelul
42 din secțiunea „ Înregistrări ale modulului text sursă ”. Secțiunea „ Static Semantics:
ExportEntries ” are mai multe detalii. Puteți vedea că intrările de export sunt configurate static
(înainte de a evalua modulul), evaluarea declarațiilor de export este descrisă în secțiunea
„ Runtime Semantics: Evaluare ”.

16.8 Obiective de proiectare pentru modulele ES6


Dacă doriți să înțelegeți modulele ECMAScript 6, vă ajută să înțelegeți ce obiective au influențat
designul lor. Cele mai importante sunt:

Exporturile implicite sunt favorizate

Structura modulului static

Asistență atât pentru încărcare sincronă, cât și asincronă

Suport pentru dependențe ciclice între module

Următoarele subsecțiuni explică aceste obiective.

16.8.1 Exporturile implicite sunt favorizate


Sintaxa modulului care sugerează că exportul implicit „este”, modulul poate părea un pic ciudat,
dar are sens dacă luați în considerare faptul că un obiectiv principal de proiectare a fost acela de
a face exporturile implicite cât mai convenabile. Citându-l pe David Herman :

ECMAScript 6 favorizează stilul de export unic / implicit și oferă cea mai dulce sintaxă la importul
implicit. Importul exporturilor numite poate și chiar ar trebui să fie puțin mai puțin concis.

16.8.2 static Structura modulului


Formatele actuale ale modulelor JavaScript au o structură dinamică: Ceea ce este importat și
exportat se poate schimba la runtime. Unul dintre motivele pentru care ES6 a introdus propriul
format al modulului este de a permite o structură statică, care are mai multe avantaje. Dar
înainte de a intra în aceste, să examinăm ce înseamnă structura statică.

Înseamnă că puteți determina importurile și exporturile la timp de compilare (static) - trebuie


doar să vă uitați la codul sursă, nu trebuie să îl executați. ES6 aplică acest lucru sintactic: Puteți
importa și exporta numai la nivelul superior (nu s-a cuibărit niciodată într-o declarație
condiționată). Și declarațiile de import și export nu au părți dinamice (nu sunt permise variabile
etc.).

Următoarele sunt două exemple de module CommonJS care nu au o structură statică. În primul
exemplu, trebuie să rulați codul pentru a afla ce importă:

var my_lib;

if (Math.random()) {

my_lib = require('foo');

} else {

my_lib = require('bar');

În al doilea exemplu, trebuie să rulați codul pentru a afla ce exportă:

if (Math.random()) {

exports.baz = ···;

Modulele ECMAScript 6 sunt mai puțin flexibile și te obligă să fii static. Drept urmare, obțineți
mai multe beneficii, care sunt descrise în continuare.

16.8.2.1 Avantaj: eliminarea codului mort în timpul pachetului


În dezvoltarea de frontend, modulele sunt de obicei gestionate după cum urmează:
În timpul dezvoltării, există coduri la fel de multe module, adesea mici.

Pentru implementare, aceste module sunt incluse în câteva fișiere, relativ mari.

Motivele pentru pachet sunt:

Mai puține fișiere trebuie recuperate pentru a încărca toate modulele.

Comprimarea fișierului pachet este puțin mai eficientă decât comprimarea fișierelor separate.

În timpul grupării, exporturile neutilizate pot fi eliminate, ceea ce poate duce la economii de
spațiu semnificative.

Motivul nr. 1 este important pentru HTTP / 1, unde costul pentru solicitarea unui fișier este
relativ mare. Acest lucru se va schimba cu HTTP / 2, motiv pentru care acest motiv nu contează
acolo.

Motivul nr. 3 va rămâne convingător. Acesta poate fi realizat numai cu un format de modul care
are o structură statică.

16.8.2.2 Avantaj: pachet compact, fără format format pachet personalizat


Rollup pachetul de module a demonstrat că modulele ES6 pot fi combinate eficient, deoarece
toate se încadrează într-un singur domeniu (după redenumirea variabilelor pentru a elimina
confruntările cu numele). Acest lucru este posibil datorită a două caracteristici ale modulelor
ES6:

Structura lor statică înseamnă că formatul pachetului nu trebuie să țină cont de module
încărcate condiționat (o tehnică obișnuită pentru a face acest lucru este introducerea codului
modulului în funcții).

Importurile fiind afișate numai în citire despre exporturi înseamnă că nu trebuie să copiați
exporturile, puteți să le faceți referire direct la acestea.

Ca exemplu, luați în considerare următoarele două module ES6.


// lib.js

export function foo() {}

export function bar() {}

// main.js

import {foo} from './lib.js';

console.log(foo());

Rollup-ul poate grupa aceste două module ES6 în următorul modul ES6 unic (rețineți exportul
neutilizat neutilizat bar):

function foo() {}

console.log(foo());

Un alt avantaj al abordării Rollup este că pachetul nu are un format personalizat, ci este doar un
modul ES6.

16.8.2.3 Avantaj: căutare rapidă a importurilor


Dacă aveți nevoie de o bibliotecă în CommonJS, primiți înapoi un obiect:

var lib = require('lib');

lib.someFunc(); // property lookup

Astfel, accesarea unui export numit prin intermediul lib.someFuncînseamnă că trebuie să


efectuați o căutare a proprietății, care este lentă, deoarece este dinamică.

În schimb, dacă importați o bibliotecă în ES6, cunoașteți static conținutul acesteia și puteți
optimiza accesele:
import * as lib from 'lib';

lib.someFunc(); // statically resolved

16.8.2.4 Avantaj: verificare variabilă


Cu o structură a modulului static, știți întotdeauna care variabile sunt vizibile în orice locație din
modul:

Variabile globale: din ce în ce, singurele variabile complet globale vor proveni din limbajul
propriu-zis. Toate celelalte vor proveni din module (inclusiv funcționalitate din biblioteca
standard și browser). Adică cunoașteți static toate variabilele globale.

Importul modulelor: știi, de asemenea, statistic.

Variabilele locale-modul: pot fi determinate prin examinarea statică a modulului.

Acest lucru ajută enorm la verificarea dacă un anumit identificator a fost scris corect. Acest tip de
verificare este o caracteristică populară a garniturilor precum JSLint și JSHint; în ECMAScript 6,
cea mai mare parte a acestuia poate fi efectuată de motoare JavaScript.

În plus, orice acces al importurilor numite (cum ar fi lib.foo) poate fi, de asemenea, verificat
static.

16.8.2.5 Beneficiu: gata pentru macros


Macro-urile sunt încă pe foaia de parcurs pentru viitorul JavaScript. Dacă un motor JavaScript
acceptă macrocomenzi, puteți adăuga noi sintaxă prin intermediul unei biblioteci. Sweet.js este
un sistem macro experimental pentru JavaScript. Următorul este un exemplu din site-ul web
Sweet.js: o macrocomenziă pentru cursuri.

// Define the macro

macro class {

rule {

$className {

constructor $cparams $cbody


$($mname $mparams $mbody) ...

} => {

function $className $cparams $cbody

$($className.prototype.$mname

= function $mname $mparams $mbody; ) ...

// Use the macro

class Person {

constructor(name) {

this.name = name;

say(msg) {

console.log(this.name + " says: " + msg);

var bob = new Person("Bob");

bob.say("Macros are sweet!");

Pentru macro-uri, un motor JavaScript efectuează un pas de preprocesare înainte de compilare:


Dacă o secvență de jetoane în fluxul de jetoane produs de parser se potrivește cu modelul
macro-ului, acesta este înlocuit de jetoane generate prin corpul macro-ului. Pasul de
preprocesare funcționează numai dacă puteți găsi în mod static definiții macro. Prin urmare,
dacă doriți să importați macro-uri prin module, acestea trebuie să aibă o structură statică.

16.8.2.6 Avantaj: gata pentru tipurile


Verificarea statică a tipului impune constrângeri similare cu macro-urile: se poate face numai
dacă definițiile de tip pot fi găsite în mod static. Din nou, tipurile pot fi importate din module
numai dacă au o structură statică.

Tipurile sunt atrăgătoare, deoarece permit dialectele rapide de tip JavaScript, în care poate fi
scris codul critic pentru performanță. Un astfel de dialect este JavaScript de nivel scăzut (LLJS).

16.8.2.7 Avantaj: sprijinirea altor limbi


Dacă doriți să suportați compilarea limbilor cu macro și tipuri statice pentru JavaScript, modulele
JavaScript ar trebui să aibă o structură statică, din motivele menționate în cele două secțiuni
anterioare.

16.8.2.8 Sursa acestei secțiuni


„ Rezoluția modulului static ” de David Herman

16.8.3 Asistență atât pentru încărcare sincronă cât și asincronă


Modulele ECMAScript 6 trebuie să funcționeze independent dacă motorul încarcă module în
mod sincron (de ex. Pe servere) sau asincron (de exemplu, în browsere). Sintaxa sa este potrivită
pentru încărcarea sincronă, încărcarea asincronă este activată de structura sa statică: Deoarece
puteți determina static toate importurile, le puteți încărca înainte de a evalua corpul modulului
(într-o manieră care amintește modulele AMD).

16.8.4 Suport pentru dependențele ciclice dintre modulele


Suportul pentru dependențele ciclice a fost un obiectiv esențial pentru modulele ES6. Iată de ce:

Dependențele ciclice nu sunt în mod inerent rele. Mai ales pentru obiecte, uneori chiar îți dorești
acest tip de dependență. De exemplu, în unii arbori (cum ar fi documentele DOM), părinții se
referă la copii și copiii se referă înapoi la părinți. În biblioteci, puteți evita de obicei dependențele
ciclice printr-un design atent. Cu toate acestea, într-un sistem mare, acestea se pot întâmpla, mai
ales în timpul refactorizării. Atunci este foarte util dacă un sistem de module le acceptă,
deoarece sistemul nu se sparge în timp ce refactorizați.
Documentația Node.js recunoaște importanța dependențelor ciclice și Rob Sayre oferă dovezi
suplimentare :

Punct de date: Am implementat o dată un sistem precum [module ECMAScript 6] pentru Firefox.
M - am cerut sprijin de dependență ciclică de 3 săptămâni după transport maritim.

Sistemul pe care l-am inventat Alex Fritze și pe care am lucrat nu este perfect, iar sintaxa nu este
foarte drăguță. Dar încă se obișnuiește 7 ani mai târziu, așa că trebuie să fi obținut ceva corect.

16.9 Întrebări frecvente: module


16.9.1 Pot folosi o variabilă pentru a specifica din ce modul doresc să
import?
Instrucțiunea importeste complet statică: specificatorul modulului său este întotdeauna fix. Dacă
doriți să determinați dinamic ce modul de încărcat, trebuie să utilizați API-ul programatic de
încărcare :

const moduleSpecifier = 'module_' + Math.random();

System.import(moduleSpecifier)

.then(the_module => {

// Use the_module

})

16.9.2 Pot importa un modul condiționat sau la cerere?


Declarațiile de import trebuie să fie întotdeauna la nivelul superior al modulelor. Asta înseamnă
că nu le puteți cuibări în ifinstrucțiuni, funcții, etc. De aceea, trebuie să utilizați API-ul
programatic de încărcare dacă doriți să încărcați un modul condiționat sau la cerere:

if (Math.random()) {
System.import('some_module')

.then(some_module => {

// Use some_module

})

16.9.3 Pot folosi variabile într-o importdeclarație?


Nu, nu poți. Amintiți-vă - ceea ce este importat nu trebuie să depindă de nimic care este calculat
la rulare. Prin urmare:

// Illegal syntax:

import foo from 'some_module'+SUFFIX;

16.9.4 Pot folosi distrugerea într-o importdeclarație?


Nu, nu poți. importDeclarația arata doar ca destructurare, dar este complet diferit (statice,
importurile sunt vederi etc.).

Prin urmare, nu puteți face așa ceva în ES6:

// Illegal syntax:

import { foo: { bar } } from 'some_module';

16.9.5 Sunt necesare exporturile numite? De ce nu exportați obiectele


implicit?
Poate vă întrebați - de ce avem nevoie de exporturi numite dacă am putea pur și simplu obiecte
de export implicit (cum ar fi în CommonJS)? Răspunsul este că nu puteți impune o structură
statică prin intermediul obiectelor și puteți pierde toate avantajele asociate (care sunt explicate
în acest capitol ).

16.9.6 Pot eval()codul modulului?


Nu, nu poți. Modulele sunt pentru o construcție la nivel prea înalt eval(). Încărcător Modulul API
oferă mijloacele pentru a crea module de siruri de caractere. În mod sintactic, eval()acceptă
scripturi (care nu permit importși export), nu module.

16.10 Avantajele ECMAScript 6 module


La prima vedere, faptul că modulele încorporate în ECMAScript 6 pot părea o caracteristică
plictisitoare - până la urmă, avem deja mai multe sisteme de module bune. Dar modulele
ECMAScript 6 au câteva funcții noi:

Sintaxă mai compactă

Structura modulului static (care ajută la eliminarea codului mort, optimizări, verificare statică și
multe altele)

Asistență automată pentru dependențele ciclice

De asemenea, modulele ES6 vor pune capăt fragmentării dintre standardele actuale dominante
CommonJS și AMD. A avea un singur standard nativ pentru module înseamnă:

Nu mai există UMD ( definiția modulului universal ): UMD este un nume pentru tiparele care
permit același fișier să fie utilizat de mai multe sisteme de module (de exemplu, CommonJS și
AMD). Odată ce ES6 este singurul modul standard, UMD devine învechită.

Noile API-uri ale browserului devin module în loc de variabile sau proprietăți globale ale
navigator.

Nu mai există obiect-as-namespaces: Obiecte precum Mathși JSONservesc ca spații de nume


pentru funcțiile din ECMAScript 5. În viitor, o astfel de funcționalitate poate fi furnizată prin
module.

16.11 Citiți în continuare


CommonJS versus ES6: „ Module JavaScript ” (de Yehuda Katz ) este o introducere rapidă pentru
modulele ECMAScript 6. Mai ales interesant este o a doua pagină în care modulele CommonJS
sunt afișate cot la cot cu versiunile lor ECMAScript 6.

S-ar putea să vă placă și