Развитие языка 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)


  1. faiwer
    12.10.2021 22:43
    +15

    // === module1.js
    export class First {}
    export class Second {}
    export class Third {}
    
    // === module2.js
    import { First } from './module1.js';
    
    First.whatever();
    
    // === module3.js
    import * as namespace from './module1.js';
    
    new namespace.Second();
    
    // === module4.js
    export * as namespace from './module1.js';

    Вылезайте из бункера, немцы ушли :-)



    1. funca
      12.10.2021 23:25
      +2

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

      Тот же Typescript поддерживает обе концепции, но по-моему на практике модули используются гораздо чаще.


      1. faiwer
        12.10.2021 23:38
        +3

        Модуль это просто вырожденный случай, где файл образует отдельный одноуровневый namespace.

        Учитывая что есть re-export-ы, вы можете организовать какую-угодно матрёшку любой вложенности и структуры. Dead code elimination вам за это спасибо не скажет, но тем не менее.


        Тот же Typescript поддерживает обе концепции, но по-моему на практике модули используются гораздо чаще.

        Потому что namespace-ы получились какими-то шибко неудобными и контринтуитивными.


      1. justboris
        13.10.2021 21:18

        Тот же Typescript поддерживает обе концепции, но по-моему на практике модули используются гораздо чаще.

        А все потому что в документации они так явно и пишут:

        we recommended modules over namespaces in modern code

        А неймспейсы так и остались, как историческое легаси


  1. js_n00b
    12.10.2021 23:43
    +1

    Статью ИИ писал?


  1. Rsa97
    13.10.2021 02:52
    +3

    А точка улыбнулась, и стала запятой.
    namespaceOne у вас объявлена как var, значит её легко переписать в любом месте кода, потеряв весь ваш якобы namespace.
    Если уж колхозить что-то, то, IMHO, лучше так:

    const namespaceOne = (() => {
      class First {};
      class Second {};
      class Third {};
      return { First, Second, Third };
    })();

    Но, в целом, хватает и возможностей модулей.


  1. 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. А ещё у каждого модуля есть свой адрес, уникальный в пределах приложения.


  1. idneutrino24 Автор
    13.10.2021 10:00
    +3

    Спасибо. Комментарии учитываются и отражаются на статье. Чтобы каша из топора была понаваристее.