Skip to main content

Import / export


Простой и дефолтный

script1.js
// простой именованный экспорт
export const a = 123;

const b = 123;

// дефолтный экспорт
export default b;

Если экспортируется несколько переменных, то дефолтные должны быть в начале. Если export без default, то импортировать нужно в фигурных скобках.

script2.js
import b, { a } from './script1';
...

import as / export as

Дефолтные импорты можно переименовывать сразу при импорте, а простые через as.

script2.js
import c, { a as d } from './script1.js';
...

Мульти экспорт объектом. Простые экспорты можно также переименовывать через as.

script1.js
const a = 1;
const b = 2;

export {
a,
b as c, // экспорт с переименованием (не работает с default)
}

import * as data

Импорт всех экспортов файла в объект.

script2.js
import * as data from './script1.js';

console.log('data', data); // { a: 1, default: 2 }
console.log('a', data.a); // 1
console.log('b', data.default); // 2

Псевдомодульные скрипты

Как пользовались раньше

file1.js
'use strict';

const TAG = 'div';

const element = document.createElement(TAG);

element.textContent = 'Мой первый тег';
document.body.appendChild(element);
file2.js
'use strict';

const TAG = 'div';

const element = document.createElement(TAG);

element.textContent = 'Мой второй тег';
document.body.appendChild(element);
index.html
...
<script src="file1.js"></script>
<script src="file2.js"></script>

Будет возникать ошибка, так как имеются одинаковые названия переменных (const TAG). И при подключении файлов они окажутся в одной области видимости, и интерпритатор выдаст ошибку

Uncaught SyntaxError: Identifier 'TAG' has already been declared

Но придумали один хак на замыканиях, можно в каждом файле изолировать область видимости функций с помощью паттерна IIFE — самовызывающиеся функции. Любые переменные внутри IIFE невидимы для внешнего мира:

file1.js
'use strict';

(function () {
const TAG = 'div';

const element = document.createElement(TAG);

element.textContent = 'Мой первый тег';
document.body.appendChild(element);
})();
file2.js
'use strict';

(function () {
const TAG = 'div';

const element = document.createElement(TAG);

element.textContent = 'Мой второй тег';
document.body.appendChild(element);
})();
index.html
...
<script src="file1.js"></script>
<script src="file2.js"></script>

Теперь все ОК, скрипты выполняют свою задачу


windw.utils

Можно сохранять функции в глоб. объект window (чтобы можно было их использовать из любого места).

file1.js
window.utils = (function () {
// Здесь суперреализации функций, которые хочется переиспользовать в проекте
function mySuperFunc1() {
console.log('utils.mySuperFunc1');
}

function mySuperFunc2() {
console.log('utils.mySuperFunc2');
}

return {
mySuperFunc1,
mySuperFunc2,
};
})();
file2.js
  (function () {
// Здесь какая-то суперреализация функций, которые хочется переиспользовать много где в проекте

function myOtherSuperFunc1() {
window.utils.mySuperFunc1(); // Вызывали сохраненную функцию
console.log('otherUtils.myOtherSuperFunc1');
}

function myOtherSuperFunc2() {
console.log('otherUtils.myOtherSuperFunc2');
}

myOtherSuperFunc1();

// Можно сохранять в кастомные поля window
window.otherUtils = {
myOtherSuperFunc1,
myOtherSuperFunc2,
};

// или так
window.myOtherSuperFunc1 = myOtherSuperFunc1;
window.myOtherSuperFunc2 = myOtherSuperFunc2;
})();

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

Еще пример

dom.js
(function () {
const TAG = 'div';

function createElement(tag = TAG, content) {
const element = document.createElement(tag);
element.textContent = content;
return element;
}

window._createElement = createElement;
})();
render.js
(function () {

const TAG = 'p';

function renderDOM(selector, content) {
const root = document.querySelector(selector);

if (!root) {
return;
}

const element = window._createElement(TAG, content); // createElement из файла dom.js
root.appendChild(element);
}
window._renderDOM = renderDOM;
})();

reverse.js
(function () {
function reverse(str) {
return str.split('').reverse().join('');
}

window._reverse = reverse;
})();
index.html
<body>
<div class="root"></div>

<script src="dom.js"></script>
<script src="render.js"></script>
<script src="reverse.js"></script>
<script>
window._renderDOM('.root', window._reverse('sseccus'));
</script>
</body>

Варианты сохранения кастомных полей в window

window._reverse = reverse; // window._reverse();

window._reverse = { reverse }; // window._reverse.reverse();

// window._reverse.reverse();
window._reverse = (function () {
function reverse(str) {
return str.split('').reverse().join('');
}

return { reverse };
})();

Модульные скрипты

Позволяет писать изолированные и атомарные модули. В них могут присутствовать переменные с одинаковыми названиями, но они будут друг от друга изолированы по области видимости.

В браузерных скриптах требуется указать аттрибут type="module". При локальносм использовании не будет работать (сработает CORS), нужно поднимать сервер.

...
<script src="./script1.js" type="module"></script>
<script src="./script2.js" type="module"></script>
</body>

Модули будут работать только с директивой 'use strict'. export помечает, какие фрагменты кода должны быть доступны вне модуля;

script1.js
'use strict';

export const a = 1;
export const b = 1;
export const c = 3;

export default { a, b, c };

import подключает части модуля, размеченные через export. Дефолтный импорт можно именовать как угодно, именованные должны совпадать, но можно изменять их название через as.

script2.js
'use strict';

import defaultImport, { a, b, c, c as copyOfC } from './script1.js';

console.log('defaultImport', defaultImport); // { 1, 2, 3 }

console.log('a', a); // 1
console.log('b', b); // 2
console.log('c', c); // 3
console.log('copyOfC', copyOfC); // 3

Ошибка с зацикливанием импортов

file1.js
import { testFile2 } from "./file1";

export function testFile1() {
console. log (testFile2());
return 42;
}

testFile1();
file2.js
import { testFile1 } from "./file2"

export function testFile2() {
console.log(testFile1());
return 10;
}

Так, как показано на примере выше, — делать не стоит. Ваш браузер начнёт «бегать по кругу» до не очень победного конца, то есть пока не переполнится стек вызовов.