Развитие языка javascript переносит выполнение кодов на распределенную сеть пользователей и снимает нагрузку с сервера. Это разумный подход. Введение в js ключевых слов class, extends и static дало возможность легко моделировать классами и объектами предметную область проекта. Это замечательно. Следующий вопрос, который необходимо рассмотреть при возрастании сложности js-проекта - это пространство имен. Именно оно позволяет избежать конфликтов с ворохом разнородных js-скриптов. Как показывает практика с этим вопросом почему-то возникли большие сложности. Смотрим ссылки ниже:
https://support.qbpro.ru/index.php?title=Базовые_Namespace_паттерны_JavaScript
https://habr.com/ru/post/527610/
https://drbrain.ru/articles/js-namespaces/
https://stackoverflow.com/questions/881515/how-do-i-declare-a-namespace-in-javascript
Плохо, только запутали все и многие решения сложны.
Есть еще и стандартная схема с использованием import, export модулей:
https://learn.javascript.ru/modules-intro
https://habr.com/ru/company/domclick/blog/532084/
За модулями конечно будущее, но с ними возникает пара вопросов - это локальное их использование без сервера (CORS policy), а главное поддержка их браузерами все еще не завершена (многопоточно web-workers не обработают модули, старые браузеры тоже не понимают):
https://developer.mozilla.org/ru/docs/Web/JavaScript/Guide/Modules
Многопоточный веб-worker по-своему захватывает скрипты:
https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/importScripts
Ладно, учтем комментарии и выберем для сравнения схему (паттерн) пространства имен, такую что многопоточные workers будут довольны и интернет заработает. Для улучшенного понимания остановимся на варианте где количество стрелочек, скобочек и прочих засоряющих человеческое восприятие закорючек минимально.
/**
* @namespace namespaceOne
*/
const namespaceOne = new function(){
class First {
//статическое свойство
static className = "First";
constructor() {
console.log("class First consructed");
}
print() {
console.log("First print");
}
//статическая функция
static printStatic() {
console.log(First.className + " static print ");
}
}
//наследование
class Second extends First {
constructor() {
super();
Second.className = "Second";
console.log("class Second consructed");
}
//полиморфизм
print() {
console.log(Second.className + " print");
}
}
class Third {
className;
encapsulation;
constructor() {
this.className = "Third";
this.encapsulation = new Second(); //инкапсуляция
console.log("class Third consructed");
}
print() {
console.log("Third print");
}
}
console.log("namespaceOne code done");
//экспорт пространства имен
return {First: First, Second: Second, Third: Third};
};
/**
* @namespace namespaceTwo
*/
const namespaceTwo = new function(){
class First {
constructor() {
console.log("other class First consructed");
}
print() {
console.log("other First print");
}
}
//экспорт пространства имен
return {First: First};
};
/**
* @namespace namespaceThree
*/
const namespaceThree = new function(){
//импорт пространства имен
namespaceOne.First.printStatic();
let second = new namespaceOne.Second();
second.print();
let ThirdClass = namespaceOne.Third;
let third = new ThirdClass();
third.print();
let first1 = new namespaceOne.First();
let first2 = new namespaceTwo.First();
first1.print();
first2.print();
};
Теперь можно смело добавлять ваш код в любой проект. С большой вероятностью конфликтов не возникнет. Минус такого решения: надо добавлять комментарии, что вот такие конструкции: const namespaceYou = new function(){...} -
являются namespace, а не чудной функцией.
Есть и более простой вариант не засорять общее пространство кодом (некоторая экологичность так сказать) - это просто взять ваш код в фигурные скобки { } и не злоупотреблять модификаторами var и function:
{ //namespace1
class Simple {
constructor() {
console.log("Simple created");
}
print() {
console.log("Simple called");
}
}
let simple = new Simple();
//экспорт
var namespace1 = {simpleObject: simple, SimpleClass: Simple};
}
{//namespace2
namespace1.simpleObject.print();
let simple = new namespace1.SimpleClass();
}
Красота.
Когда нибудь веб-workers начнут работать с модулями, а CORS policy разрешит писать вам локальные модульные js-приложения и все это уйдёт в историю. Когда-нибудь ...
Комментарии (8)
Rsa97
13.10.2021 02:52+3А точка улыбнулась, и стала запятой.
namespaceOne у вас объявлена как var, значит её легко переписать в любом месте кода, потеряв весь ваш якобы namespace.
Если уж колхозить что-то, то, IMHO, лучше так:const namespaceOne = (() => { class First {}; class Second {}; class Third {}; return { First, Second, Third }; })();
Но, в целом, хватает и возможностей модулей.
flancer
13.10.2021 07:52+1Это из вики:
In computing, a namespace is a set of signs (names) that are used to identify and refer to objects of various kinds.
Namespace'ы дают возможность однозначно идентифицировать любой объект кода (класс, константу, функцию, метод, свойство) в рамках всего проекта (приложения) или даже глобально (в рамках всех объектов кода в пределах одного ЯП). Именно в этом и вся сложность - обеспечение уникальной идентификации (в пределах пакета/проекта/глобально).
То, что вы описали в данной публикации:
Теперь вы можете смело добавлять свой код в любой проект, в любое место просто взяв его {в фигурные скобки} и все - секундное дело.
это scope. Ограничение области видимости переменных. А
var
- способ пробиться за границы блочной видимости:var namespaceOne = {First: First, Second: Second, Third: Third};
Вы должны каким-то образом обеспечить уникальность всех
var namespaceX
в пределах вашего проекта, чтобы не получилось нечто типа такого:{ class First {} var namespace = {First}; } { class Second {} var namespace = {Second}; } { const ns = namespace; // {Second} }
Вот это "каким-то образом обеспечить уникальность" и есть про namespace'ы (т.е., про идентификацию).
В общем, коллеги выше правы - используйте es-модули. У каждого модуля уже есть свой scope. А ещё у каждого модуля есть свой адрес, уникальный в пределах приложения.
idneutrino24 Автор
13.10.2021 10:00+3Спасибо. Комментарии учитываются и отражаются на статье. Чтобы каша из топора была понаваристее.
faiwer
Вылезайте из бункера, немцы ушли :-)
funca
Основные фишки namespace в том, что они могут быть иерархическими, а реализацию одного пространства имён можно раскидывать по разным файлам. Модуль это просто вырожденный случай, где файл образует отдельный одноуровневый namespace.
Тот же Typescript поддерживает обе концепции, но по-моему на практике модули используются гораздо чаще.
faiwer
Учитывая что есть re-export-ы, вы можете организовать какую-угодно матрёшку любой вложенности и структуры. Dead code elimination вам за это спасибо не скажет, но тем не менее.
Потому что
namespace
-ы получились какими-то шибко неудобными и контринтуитивными.justboris
А все потому что в документации они так явно и пишут:
А неймспейсы так и остались, как историческое легаси