Уже около полугода я пишу на ES6 (который в итоге назвали ES-2015) и ES7, с использованием бабеля в качестве транслятора. Писать мне приходилось в основном серверную часть, соответственно, использование модулей было само собой разумеющимся: до ES6 — с помощью модульной системы самой ноды, а теперь — с помощью стандартизированной семантики самого языка. И мне захотелось написать статью, в которой расписать тонкости, плюсы, подводные камни и необычности новообретенной модульной системы языка: отчасти — чтобы другим было проще, отчасти — чтобы разобраться во всём окончательно самому.

Я разберу, что такое модуль, как происходит экспорт сущностей, как происходит импорт сущностей, чем система модулей ES6 отличается от системы модулей в NodeJS.

Модуль


По сути, модуль — это инструкция (statement), которая вызывается неявно — посредством создания файла и выполнения его с помощью интерпретатора ES (прямо, при «запуске» файла программистом, или косвенно, в результате импорта другим модулем). В ES6 есть чёткое соотношение: один файл — один модуль. Каждый модуль имеет отдельную область видимости (Lexical environment) — т. е. все объявления переменных, функций и классов не будут доступны за пределами модуля (файла), если не экспортированы явно. На верхнем уровне модуля (т. е. вне других инструкций и выражений) можно использовать операторы import для импорта других модулей и их экспортируемых сущностей, и export для экспорта собственных сущностей модуля.

Оператор export


Оператор export позволяет экспортировать сущности модуля, чтобы они были доступны из других модулей. У каждого модуля есть неявный объект [[Exports]], в котором хранятся ссылки на все экспортируемые сущности, а ключом является идентификатор сущности (например, имя переменной). Это очень напоминает module.exports из модульной системы NodeJS, но [[Exports]] всегда объект, и его нельзя получить напрямую. Единственный способ его изменить — использовать оператор export.

Этот оператор имеет несколько модификаций, рассмотрим все возможные случаи.

Экспорт объявляемой сущности


По сути, это обычное объявление переменной, функции или класса, с ключевым словом "export" перед ним.

export var variable;
export const CONSTANT = 0;
export let scopedVariable = 20;
export function func(){};
export class Foo {};

В данном случае система экспортов ES6 удобнее, чем в NodeJS, где пришлось бы сначала объявить сущность, а потом добавить её в объект module.exports.

var variable;
exports.variable = variable;

const CONSTANT = 0;
exports.CONSTANT = CONSTANT;
...

Но есть и гораздо более важное различие между этими двумя системами. В NodeJS свойству объекта exports присваивается значение выражения. В ES6 оператор export добавляет в [[Exports]] ссылку (или привязку, binding) на объявленную сущность. Это значит, что [[Exports]].<имя сущности> всегда будет возвращать текущее значение этой сущности.

export var bla = 10; // [[Exports]].bla === 10
bla = 45; // [[Exports]].bla === 45

Экспорт уже объявленной сущности


Здесь всё то же самое, только экспортируем мы сущность, которая уже была объявлена выше. Для этого применяются фигурные скобки после ключевого слова export, в которых через запятую указываются все сущности (ну, их идентификаторы — например, имя переменной), которые необходимо экспортировать.

var bar = 10;
function foo() {}

export { bar, foo };

С помощью ключевого слова "as" можно «переименовать» сущность при экспорте (точнее будет сказать, изменить ключ для [[Exports]]).

var bar = 10;
function foo() {}

export { bar as bla, foo as choo };

Для такого вида экспорта также верно, что [[Exports]] хранит у себя лишь ссылку на сущность, даже в случае «переименования».

var bar = 10;

export { bar as bla }; // [[Exports]].bla === 10

bar = 42; // [[Exports]].bla === 42

Экспорт по умолчанию


Этот вариант использования export отличается от двух описанных выше, и, на мой взгляд, он немного нелогичен. Заключается он в использовании после export ключевого слова default, после которого может идти одно из трех: выражение, объявление функции, объявление класса.

export default 42; 

export default function func() {}

export default class Foo {}

Каждый из этих трех вариантов использования добавляет в [[Exports]] свойство с ключом «default». Экспортирование по умолчанию выражения (первый пример, «export default 42;») — единственный случай при использовании export, когда значением свойства [[Exports]] становится не ссылка на какую-либо сущность, а значение выражения. В случае же экспорта по умолчанию функции (не анонимной, естественно) или класса — они будут объявлены в области видимости модуля, а [[Exports]].default будет ссылкой на эту сущность.

Оператор import


Чтобы не разрывать повествование, продолжу сразу об импорте по умолчанию.

Экспортированное по умолчанию свойство считается «главным» в этом модуле. Его импорт осуществляется с помощью оператора import следующей модификации:

import <любое имя> from '<путь к модулю>';

В этом вся польза дефолтного экспорта — при импорте можно назвать его, как угодно.

// sub.js
export default class Sub {};

// main.js
import Base from './sub.js'; // И да, иногда это может сбить столку, поэтому лучше всё же использовать имя модуля

Импорт обычных экспортируемых свойств выглядит немного иначе:

// file1.js
export let bla = 20;

// file2.js
import { bla } from './file1.js'; // нужно точно указать имя сущности

// file3.js
import { bla as scopedVariable } from './file1.js'; // но можно переименовать

Рассмотрим модуль «file2.js». Оператор import получает объект [[Exports]] импортируемого модуля ('file1.js'), находит в нём нужное свойство («bla»), а после создаёт привязку идентификатора "bla" к значению [[Exports]].bla. Т. е., точно так же, как и [[Exports]].bla, bla в модуле «file2.js» всегда будет возвращать текущее значение переменной «bla» из модуля «file1.js». Равно как и scopedVariable из модуля «file3.js».

// count.js
export let counter = 0;

export function inc() {
  ++counter;
}

// main.js
import { counter, inc } from './count.js'; 

console.log(counter); // 0
inc();
console.log(counter); // 1

Импорт всех экспортируемых свойств


import * as sub from './sub.js';

По сути, таким образом мы получаем копию [[Exports]] модуля «sub.js».

Включение модуля без импорта


Иногда бывает нужно, чтобы файл просто запустился.

import './worker';

Реэкспорт


Последняя вещь, которую я здесь рассмотрю — это повторный экспорт модулем свойства, которое он импортирует из другого модуля. Осуществляется это оператором export.

// main.js
export { something } from './another.js';


Два замечания, которые тут стоит сделать: первое — something после реэкспорта НЕ становится доступной внутри модуля main.js, для этого придётся сделать отдельный импорт (уж не знаю, почему так, видимо, чтобы сохранить дух оператора export); и второе — система ссылок работает и тут: модуль, который импортирует из «main.js» something, будет получать актуальное значение переменной something в «another.js»;

Так же можно зареэкспортить все свойства из другого модуля.

export * from './another';

Однако важно помнить, что если вы объявите в своём модуле экспорт с таким же именем, как у реэкспортного, то он затрёт реэскпортное.

// sub.js
export const bla = 3, foo = 4;

// another.js
export const bla = 5;
export * from './sub';

// main.js
import * as another from './another';
console.log(another); // { bla: 5, foo: 4 }

Это решается переименованием конфликтных свойств при реэкспорте.

И, почему-то, синтаксиса для реэкспорта дефолтных свойств у export нет, но можно сделать так:

export { default as sub } from './sub';

Несколько слов о свойствах импортов


Поддержка циклических ссылок


Собственно, вся эта пляска с биндингами вместо присвоения нужна для нормального разрешения циклических ссылок. Т. к. это не значение (которое может быть и undefined), а ссылка на то место, где когда-то что-то будет лежать — ничего не упадёт, даже если цикл.

Импорты всплывают


Импорты «всплывают» наверх модуля.
// sub.js
console.log('sub');

// main.js
console.log('main');
import './sub.js';

Если запустить main.js, то в консоль сначала выведется «sub», и только потом «main» — именно из-за всплытия импортов.

Экспорт по умолчанию — это ещё не конец


Вот такие конструкции вполне допустимы.

// jquery.js
export default function $() {}
export const AUTHOR = "Джон Резиг";

// main.js
import $, { AUTHOR } from 'jquery.js';


И вообще, по сути, default — это просто ещё один именованный экспорт.

import Base from './Base';

То же самое, что и
import { default as Base } from './Base';




Спасибо большое за прочтение статьи, надеюсь, она сэкономит вам время и вообще поможет. Буду рад услышать вопросы и помочь).

Комментарии (38)


  1. hell0w0rd
    25.09.2015 02:01
    +2

    На мой взгляд самый неочевидный момент с модулями, после common.js:
    export const foo = bar; !== exports.foo = bar;
    То есть:

    // lib.js
    export function foo() {}
    
    // app.js
    import lib from './lib';
    
    console.log(lib.foo); // undefined
    

    Это можно понять и по спекам и по вашей статье, но просто это немного необычно, после common.js, так скажем не ожидаемо. Но это легко исправить: import * as lib from './lib';


    1. auine
      25.09.2015 07:41
      +2

      Ну так common Modules !== es6))

      Для этого есть export default, а не *. А вы превели именованый экспорт, разумеется, что там будет undef, что же еще?


    1. trikadin
      25.09.2015 11:32
      +1

      Честно говоря, мне тоже дефолт экспорт кажется местами нелогичным, но уж как сделали — так сделали. Бабель сейчас такое поведение фолбэчит — модулям, которые изначально написаны на ES6, он ставит не-enumerable экспортируемое свойство __esModule = true, и если потом при импорте модуля он видит, что такое свойство не импортируется, то он смотрит: если есть с именем default, то вернёт default, если нету — вернёт exports того модуля целиком. Но гораздо правильней и безопасней делать вот так:

      import * as fs from 'fs';
      


      Хотя и непривычно, конечно))


  1. offline15
    25.09.2015 08:06

    А нет ли способов реэкспортировать нужные объекты прямо из под имени директории?
    Например есть папка components в ней есть файлы Email.js и Inbox.js в которых есть классы Email и Inbox соответсвенно. Мне бы хотелось чтоб я мог их импортировать с помощью
    import Components
    ну или import Components from 'components'
    В python это возможно сделать с помощью создания __init__.py файла в папке components и импорта нужных вещей туда. Нет ли в JS какого нибудь такого файла в стиле index.js который автоматически читается если сделать import этой папке?


    1. flops
      25.09.2015 10:09

      Commonjs в ноде при указании дерриктории действительно пытается заимпортить index.js, import в babel как раз делает фолбэк для импорта через require, но вот как и во что это выльется на клиенте зависит уже от библиотеки\сборщика который вы используете.
      Плюс в отличие от питона нет никакой явной нотации на то как должен выглядеть index.js в таком случае. Не натыкался на то как бы это выглядело в спеках, если кто-то знает хотел бы прочитать про подобный вариант. Но в целом с реэкспортом можно весьма удобно собирать такие компоненты.


      1. offline15
        25.09.2015 20:21

        Не совсем понял, что в таком случае делает babel. Использую именно его с browserify. Могу ли я пользоваться вариантом с index.js в этом случае? Пишу для браузера (Reactjs), но всё склеиваю в один файл.


        1. trikadin
          25.09.2015 21:05

          Можете.

          Поведение импорта в ноде и в ES6 отличается, бабель делает так, чтобы вы этого не заметили. Лучший вариант, чтобы понять, как — это посмотреть на результат работы бабеля)


  1. kashey
    25.09.2015 08:54
    +1

    Только мне до сих пор не понятно как в браузер будут приходить сотни и миллионы маленьких файликов-модулей. Сколько лет разными средствами от этого убегали, и вот вам :(
    Service workers для этих задач поднимать как-то странно. Вещи не совсем связанные.


    1. janitor
      25.09.2015 09:24
      +1

      HTTP/2 должен решить эту проблему


      1. faiwer
        25.09.2015 09:32
        +3

        Мне кажется, что помимо накладных расходов на сами запросы, есть ещё накладные расходы на использование файловой системы. Так что вопрос объединения модулей в 1 файл, всё же стоит и с HTTP/2. IMHO


        1. janitor
          25.09.2015 09:45

          Мне кажется, тут все очень зависит от проекта. В каких-то проектах необходимо все объединять в один файл, в каких-то лучше все как-то хранить модульно. Например, на прошлой работе все сжималось в один (или два, точно не помню) файла, общий размер которого около 2мб (одного ExtJS фиг знает сколько было). В проекте были разные тарифные планы, и, например, фришный пользователь мог видеть только 20% от всего приложения. Получается, что для него грузилась куча ненужного кода.
          А расходы на использование файловой системы в обычном среднестатистическом проекте — микроскопические. Картинок всяких грузится обычно в разы больше, чем скриптов. Да и никто ведь разом не будет загружать все 100 модулей. А вот если будет — то да, лучше в один файл все :)


          1. kashey
            25.09.2015 09:56
            +1

            Пример — АПИ Яндекс.Карт состоит из нескольких тысяч модулей.
            Вообще проекты следующие KISS и вообще правилам декомпозиции любят состоять из кучи маленьких «reusable» кусочков.


            1. janitor
              25.09.2015 10:56

              Про количество модулей в Картах в курсе :)
              Ну в картах и есть несколько разных сборок (вроде бы) с необходимой для каждого юзера функциональностью. Понятно, что тут такой подход и нужен — все нужные модули в один файл :)


          1. faiwer
            25.09.2015 09:57

            например, фришный пользователь мог видеть только 20% от всего приложения. Получается, что для него грузилась куча ненужного кода.
            Не понимаю, причём тут это. Что мешало сделать несколько bundle's, самый простой из которых загружал только нужное обычному посетителю, в обычных ситуациях, а менее популярные зависимости раскидать по другим bundle-ам?

            А расходы на использование файловой системы в обычном среднестатистическом проекте — микроскопические. Картинок всяких грузится обычно в разы больше, чем скриптов.
            Почему же? Если речь идёт о вёрстке, то разработчики, зачастую, используют спрайты или base64 dataurl, и число запростов уменьшается многократно (вплоть до 1-го запроса к CSS файлу, где всякая мелочь лежит как base64). А картинок связанных с контентом web-сайта может и не быть вовсе. Или быть мало, не всё же новостные порталы.

            Да и никто ведь разом не будет загружать все 100 модулей.
            Ну у меня в проектах обычно только SCSS файлов около 100-ни, если не больше (import-ы). да и JS-ов на данный момент порядка 80-и.


            1. janitor
              25.09.2015 10:53

              Не понимаю, причём тут это. Что мешало сделать несколько bundle's, самый простой из которых загружал только нужное обычному посетителю, в обычных ситуациях, а менее популярные зависимости раскидать по другим bundle-ам?

              Я привел пример, когда подход «все в одном файле» был неоправдан. Мешало то, что проект очень древний (перепиленный ExtJS 3.4) и как такового загрузчика, вроде RequireJS, не было. И не было нормального разбиения по модулям, все друг с другом было очень связано. Переписывать и переделывать — очень дорого для бизнеса + получился бы велосипед с треугольными колесами (хотя, на мой взгляд, поддерживать все эту хрень стоит еще дороже, чем переписать).
              Почему же? Если речь идёт о вёрстке, то разработчики, зачастую, используют спрайты или base64 dataurl, и число запростов уменьшается многократно (вплоть до 1-го запроса к CSS файлу, где всякая мелочь лежит как base64). А картинок связанных с контентом web-сайта может и не быть вовсе. Или быть мало, не всё же новостные порталы.

              Тут не про верстку имелось в виду. Не обязательно бть новостным порталом, чтобы грузить много изображений. Возьмите тот же Яндекс.Диск, Flickr, Pinterest, какая-нибудь CRM-ка (например, какие-нибудь аватары пользователей). Ну 30-40 картинок может быть легко, и на этом фоне загрузка 5-10 скриптов — мелочь, тем более Вы же знаете про кэширование?
              Ну у меня в проектах обычно только SCSS файлов около 100-ни, если не больше (import-ы). да и JS-ов на данный момент порядка 80-и.

              SCSS тут причем? И вы разом загружаете пользователю все эти 80 js-модулей? Вы же сами утверждали, что необходимо загружать только нужное сначала. Сколько у Вас критически важных модулей из этих 80?


              1. faiwer
                25.09.2015 11:13

                загрузка 5-10 скриптов — мелочь, тем более Вы же знаете про кэширование
                Легко может стоить вам от 500ms до 5000ms (например, GPRS) при загрузке страницы. Это очень много. К тому же JS-функциональность всё чаще является не «рюшечками» а острой необходимостью.

                Не понимаю, что вы мне пытаетесь доказать. В то время когда некоторые разработчики заранее подготавливают gzip-ы для nginx_gzip_static, вы говорите о +-10 скриптах? :) Во frontende-е уже давно борьба идёт на уровне «спичек», а не «брёвен».

                SCSS тут причем?
                Технология позволяет использовать импорты не задумываясь о последствиях, т.к. компилятор сам всё соберёт в 1 файл. Это удобство легко приводит к тому, что SCSS-файлы плодятся в огромном количестве (просто потому что декомпозиция это очень удобно). Привёл в качестве примера, сколько может быть мини-файлов в рядовом проекте.

                И вы разом загружаете пользователю все эти 80 js-модулей? Вы же сами утверждали, что необходимо загружать только нужное сначала. Сколько у Вас критически важных модулей из этих 80?
                Пользователь загружает 1 минифицированный файл со всем необходимым. Администратор загружает 2 файла. Некоторые страницы сайта могут потребовать загрузку 3 файла (например в нём может быть огромный Ckeditor и плагины к нему).

                И всё это работает автоматически. Вы просто определяет в конфигурации сборки какие файлы в какой бандл дожны попасть. Всю чёрную работу возьмёт на себя requireJS или его аналог (да хоть самописный, какая разница). Система зависимостей в действии. Вам больше нигде не потребуется писать <script type=«text/javascript src=»..."></script> указывая требуемые файлы вручную.


                1. janitor
                  25.09.2015 13:41

                  Вас куда-то не туда понесло. Мы обсуждали, в данном контексте, насколько я понял, что если не склеивать все JS в один / несколько файлов, то будет расти нагрузка на сервере, т.к. будет много операций на чтение каждого файла.

                  Легко может стоить вам от 500ms до 5000ms (например, GPRS) при загрузке страницы. Это очень много. К тому же JS-функциональность всё чаще является не «рюшечками» а острой необходимостью.

                  Причем тут вообще GPRS? Разговор у нас шел вообще о, цитирую, «ещё накладные расходы на использование файловой системы». И все, о чем, я говорил, было связано именно с этой темой. Я не утверждал, что плохо или хорошо склеивать все в один файл — все зависит от проекта, где-то это нужно, где-то нет. А вы тут препроцессоры еще приплели :)


                  1. faiwer
                    25.09.2015 13:50

                    Нас обоих куда-то не туда понесло. Попробую заново. Модульная система располагает к тому, что число модулей. т.е. файлов, может быть очень большим. 100, 200, 1000, 10'000. Если их никак не склеивать, то нагрузка на файловую систему будет. Если отдавать их разом тем более. К тому же как правильно заметил kashey с latency не поспоришь. Т.е. если там будут иерархические ветвления include-ов, ничего хорошего из этого не выйдет.

                    Соответственно есть реальная необходимость иметь возможность объединения модулей в 1 или несколько файлов. Или размещения нескольких модулей в рамках одного файла. Учитывая, что мы обсуждаем нативный функционал, то интересует как раз нативное решение. Объединение сопряжённое с правкой исходных кодов (автозамены include-ов и export-ов) на поддерживаемые конструкции мы можем и сейчас без поддержки ES6 модулей вообще.

                    все зависит от проекта, где-то это нужно, где-то нет.
                    В некоторых проектах вообще нет подключаемых JS-ов. Там, соответственно, это не нужно. Но зачем это обсуждать? :)


      1. kashey
        25.09.2015 09:54
        +3

        Ничего кроме bulk запросов не может решить проблему временых задержек на получения данных. Прямо каноническое бутылочное горлышко получается.
        Требуется на сервере разрулить зависимости, и передать «предсказанную» кучу модулей. Иначе все будет работать как игра с DVD — вжик-вжик, вжик-вжик, ууууууу… С latency не спорят.


    1. flops
      25.09.2015 10:11

      Можете посмотреть jspm.
      Весьма удобная штука надстройка над SystemJS, которая даёт возможность импортить файлы разного «формата» + сборщик. Который на уровне импортов и конфига собирает это всё в один бандл или если хотите можете собирать это помодульно и т.д…


    1. prog666
      25.09.2015 11:47

      browserify + babelify в данный момент спасают.


      1. kashey
        25.09.2015 11:48

        Не спасают, а заменяют «нормальную» модульность ES6.


        1. prog666
          25.09.2015 12:03

          скорее эмулируют, там же тоже можно использовать import и export


    1. trikadin
      25.09.2015 11:49

      У меня есть ощущение, что эта система модулей сделана так, чтобы максимально легко транспилиться в любую другую — AMD, require, common. Поэтому можно легко писать в es6 стиле, а потом browserify или webpack.


    1. trikadin
      25.09.2015 13:11

      Упс.


    1. auine
      25.09.2015 13:58

      С ES6 мы будем не билдить, а бандлить)) Уже даже есть сборщики ES6 модулей, просто погуглите по гитхабу)


  1. faiwer
    25.09.2015 09:30
    +2

    Многое уяснил. Спасибо за статью. Осталось не совсем ясным использование «from». В случае requireJS мы может влиять на то, какие конкретно файлы будут загружаться, и нужно ли что-либо вообще догружать. А как с этим делом обстоит в ES6 модулях? Судя по тексту, каждый файл — отдельная область видимости, которые с друг другом не пересекаются, но что если речь идёт о будущем frontend-а и мы хотим разместить несколько модулей в 1 файл (к примеру, для экономии запросов)?


    1. trikadin
      25.09.2015 11:44

      Заюзайте вебпак)

      Вообще, у меня пока ощущение, что эта система модулей сделана так, чтобы с минимальными потерями транспилиться в любую другую — по сути, такой метаязык, который можно потом хоть в AMD, хоть в CommonJS, хоть в require) И это круто. Но, конечно, это мои личные ощущения.


      1. faiwer
        25.09.2015 12:59

        Заюзайте вебпак)
        Да заюзать то я могу что угодно и когда угодно. Но ведь тут речь идёт о нативном представлении, о возможности самого языка или инфраструктуры связанной с ним. К примеру, в PHP есть autoloader-ы классов. И можно одновременно использовать несколько разных лоадеров, которые могут работать по совсем разной логике (очень давно не работал с PHP, могу в чём-нибудь ошибаться).

        Вводя модульную систему в JS, подразумевая, что оная должна работать и в браузере, мы должны иметь возможность влиять на неё. Т.е. как то управлять механизмом работы этих ES6-Modules. Но пока что я ничего подобного ни в 1-ой статье не углядел.

        Робко надеюсь на то, что этот вопрос будет удачным образом решён позднее. А транспилить код туда сюда я не люблю. Чем нативнее решение, тем надёжнее, и тем удобнее его debug-ить. К примеру, недавно я стал сталкиваться в Web Developer Tools с тем, что многие ошибки на страницах детектируются не там, где они произошли, а в каком-то абстрактном xml.js (которого даже не существует), в котором нужного кода нет и в помине. Оказалось что дело в sourceMap. Или вот в nodeJS обычный require, в непонятных мне случаях, все SyntaxError-ы в моих файлах детектирует в своих внутренних файлах (таких как vm.js), и я не могу понять ни в каком файле ошибка, ни в какой строке, ни на какой позиции. Потому что в стектрейсах просто чушь. И мне приходится некоторые require оборачивать в try catch, просто для того, чтобы узнать хотя бы файл, в котором ошибка произошла (я ведь знаю, что я require-ю).

        Я предпочитаю использовать нативные решения (привет кофе-скрипт), за исключением тех случаев, где альтернатива предлагает просто бездну удобств (например scss, less и шаблонизаторы) и минимум геморроя.


        1. trikadin
          25.09.2015 13:09
          +1

          Вот здесь сейчас идёт разработка спецификации загрузчика модулей, который как раз позволит подгружать всё динамически, асинхронно и условно.

          Я сначала транспилил код, а теперь вообще пользуюсь babel-node, чтобы не прикручивать кучу галпа сверху. Не замечал проблем с дебагом. И ошибка с require у вас очень странная, не попробуете рабочий пример создать?

          И да, я заметил логическое несоответствие: вы не любите транспилить, но юзаете кофескрипт?


    1. Finom
      25.09.2015 12:27
      +1

      Очень хороший вопрос. Предполагаю, что это будущее будет решаться в будущем.


    1. asci
      25.09.2015 18:30

      Если вам надо программно управлять зависимостями вы можете использовать

      System.import
      который позволяет асинхронно загружать скрипты с интерфейсом промисов


      1. trikadin
        25.09.2015 18:53

        А вы использовали полифиллы для него? Что скажете, удобно? Я просто посматриваю, но это сильный драфт, и необходимости небольшой нет (пишу под ноду, и если не хватает где-то es6 импортов — просто юзаю require), поэтому руки не дошли нормально потыкать.


  1. affka
    25.09.2015 09:41

    Еще одним отличием (на сколько я понял) import от require() является то, что import можно располагать только в начале кода, в то время как require() может быть где угодно и подгружать модули даже динамически.
    Поправьте, если ошибаюсь.


    1. trikadin
      25.09.2015 11:41
      +1

      Да, абсолютно верно. Импорт можно располагать только на самом «верхнем» уровне модуля, т. е. нельзя вот так

      if (weNeedFS) {
        import * as fs from 'fs';
      }
      


      И даже, как я писал в статье, если написать импорт после чего-либо другого, то он всё равно «всплывет».

      Вот здесь сейчас идёт разработка спеки для загрузчика модулей, с помощью которого как раз и можно будет реализовать динамическую, условную и прочую подгрузку модулей.


  1. VolCh
    26.09.2015 10:07

    Как-то очень смущает требование один файл — один модуль для скриптового языка, код которого активно передается клиенту по сети.


    1. trikadin
      26.09.2015 17:34

      На самом деле, это не требование. Просто по факту сейчас в спецификации нет ничего, кроме описания структуры модуля и того, как устроен импорт/экспорт. А до тех пор, пока не появится описание того, как это всё должно лежать на фс, как резолвиться и так далее, реализаций у нас не будет. Поэтому пока ждём, пока они допишут стандарт (около года), и юзаем эту модульную систему, транспиля её в ноду, браузерифай и прочее.


      1. VolCh
        28.09.2015 00:48

        Честно, спеку не читал ещё, но в статьях не видел ничего подобного ключевому слову module или namespace. а без них реализация export/import (читай полноценной изоляции модулей) в пределах одного файла без компиляции в ES5 или подмножество ES6 выглядит сомнительным или неполноценным.