Як я можу оголосити простір імен у JavaScript?


990

Як створити простір імен в JavaScript, щоб мої об'єкти та функції не перезаписалися іншими одноіменними об'єктами та функціями? Я використовував таке:

if (Foo == null || typeof(Foo) != "object") { var Foo = new Object();}

Чи є більш елегантний чи стислий спосіб зробити це?


20
Я бачу, куди ви збираєтеся перевірити, чи буде взято простір імен, але оскільки об’єкт не буде створений, якщо це не вдалося, я думаю, що кращим підходом є попередження, якщо буде взято простір імен. Відверто кажучи, це просто не повинно відбуватися в більшості ситуацій із СВ, і його слід швидко вловлювати в розвиток.
annakata

18
Візьміть "простір імен" верхнього рівня (властивість вікна). Його власником. Конфлікти слід виявити на початку тестування. Не турбуйтеся додавати всі ці чеки "що якщо". Це фатальна проблема для дублікатів "просторів імен", і її слід розглядати як таку . Ви можете дотримуватися такого підходу, як jQuery, щоб дозволити користуватися користувацьким "простором імен"; але це все ще проблема дизайну.

Будь ласка, змініть прийняту відповідь на stackoverflow.com/questions/881515/… , що є набагато більш елегантним та оновленим рішенням.
hlfcoding

@pst, що з тим, що робить YUI? Я вважаю, що вони роблять саме це для поступового додавання до свого простору імен. такі хитрощі потрібні для виконання в HTTP-середовищі?
Simon_Weaver

Дивіться також stackoverflow.com/questions/2102591/… щодо питань продуктивності
Тім Абел

Відповіді:


764

Мені це подобається:

var yourNamespace = {

    foo: function() {
    },

    bar: function() {
    }
};

...

yourNamespace.foo();

62
Важливим моментом є релігійність щодо розширення не далі, ніж одна коренева змінна. З цього все має витікати.
annakata

22
Це не створює закриття вашого коду - це втомлює викликати інші ваші функції, оскільки вони завжди повинні мати вигляд: yourNamespace.bar (); Я зробив проект з відкритим кодом ПРОСТО, щоб вирішити цю проблему дизайну: github.com/mckoss/namespace .
mckoss

24
annakata: "Важливим моментом є релігійність щодо розширення не далі, ніж одна коренева змінна". - Чому це?
користувач406905

11
@alex - чому повинна бути неглибока структура об'єкта?
Райан

25
@Ryan Я мав на увазі, що все повинно бути під MyApp, наприклад, MyApp.Views.Profile = {}а не MyApp.users = {}та MyViews.Profile = {}. Не обов’язково, щоб було лише глибини двох рівнів.
алекс

1042

Я використовую підхід, знайдений на сайті jQuery Enterprise :

Ось їх приклад, який показує, як оголосити приватні та публічні властивості та функції. Все робиться як самовиконання анонімної функції.

(function( skillet, $, undefined ) {
    //Private Property
    var isHot = true;

    //Public Property
    skillet.ingredient = "Bacon Strips";

    //Public Method
    skillet.fry = function() {
        var oliveOil;

        addItem( "\t\n Butter \n\t" );
        addItem( oliveOil );
        console.log( "Frying " + skillet.ingredient );
    };

    //Private Method
    function addItem( item ) {
        if ( item !== undefined ) {
            console.log( "Adding " + $.trim(item) );
        }
    }
}( window.skillet = window.skillet || {}, jQuery ));

Тож якщо ви хочете отримати доступ до одного з громадських членів, ви просто підете skillet.fry()або skillet.ingredients.

Що насправді класно - це те, що тепер ви можете розширити простір імен за допомогою того самого синтаксису.

//Adding new Functionality to the skillet
(function( skillet, $, undefined ) {
    //Private Property
    var amountOfGrease = "1 Cup";

    //Public Method
    skillet.toString = function() {
        console.log( skillet.quantity + " " +
                     skillet.ingredient + " & " +
                     amountOfGrease + " of Grease" );
        console.log( isHot ? "Hot" : "Cold" );
    };
}( window.skillet = window.skillet || {}, jQuery ));

Третій undefined аргумент

Третій undefinedаргумент - джерело змінної значення undefined. Я не впевнений, чи це все ще актуально сьогодні, але під час роботи зі старими браузерами / стандартами JavaScript (ecmascript 5, javascript <1.8.5 ~ firefox 4) глобальна змінна область undefinedзаписується, щоб кожен міг переписати її значення. Третій аргумент (коли не передається значення) створює змінну, undefinedяку називають простором імен / функцією. Оскільки при створенні простору імен не було передано значення, воно за замовчуванням відповідає значенню undefined.


9
+1 за цей чудовий зразок. Для всіх, хто зацікавився, цей зразок був частиною чудової презентації Іллі Манор на Mix 2011 (ігноруйте заголовок) live.visitmix.com/MIX11/Sessions/Speaker/Elijah-Manor
Даррен Льюїс

11
Зі статті Іллі перераховано переваги та мінуси цього підходу. Плюси: 1. Публічні та приватні властивості та методи, 2. не використовує громіздкого OLN, 3. Захищає невизначений 4. Забезпечує, що $ посилається на jQuery, 5. Простір імен може охоплювати файли, Мінуси: важче зрозуміти, ніж OLN
Джаред Бек

4
Це називається сьогодні IIFE ( Вираз негайно викликаної функції ). Дякуємо за вашу відповідь +1!
Густаво Гондим

20
@CpILL: не впевнений, чи все ще актуальний, але третій undefinedаргумент є джерелом змінної значення undefined. Під час роботи зі старими браузерами / стандартом javascript (ecmascript 5, javascript <1.8.5 ~ firefox 4) глобальна змінна область undefinedдоступна для запису, тому кожен може переписати її значення. Додаючи третій додатковий аргумент, який ви не передаєте, робить його цінним undefined, тому ви створювали область імен, undefinedяка не переписується сторонніми джерелами.
mrówa

4
Перевага @SapphireSun window.skillet = window.skillet || {}полягає в тому, що він дозволяє безліч скриптів безпечно додавати в одне і те саме простір імен, коли вони заздалегідь не знають, в якому порядку вони будуть виконуватись. Це може бути корисним, або якщо ви хочете мати можливість довільно впорядкувати включення скриптів, не порушуючи код, або якщо ви хочете завантажувати сценарії асинхронно з атрибутом async і тому не мати гарантії щодо порядку виконання. Дивіться stackoverflow.com/questions/6439579/…
Марк Амерді

338

Ще один спосіб зробити це, який я вважаю трохи менш обмежуючим, ніж об'єктна буквальна форма, це такий:

var ns = new function() {

    var internalFunction = function() {

    };

    this.publicFunction = function() {

    };
};

Вищезазначене дуже нагадує шаблон модуля, і вам це подобається чи ні , він дозволяє розкривати всі ваші функції як публічні, уникаючи жорсткої структури об'єкта в прямому сенсі.


16
1. Існує різниця між OLN і схемою модуля. 2. Мені / завжди / не подобається OLN, як ви повинні пам’ятати, щоб не ставити останню кінцеву кому, і всі ваші атрибути повинні бути ініціалізовані зі значенням (наприклад, null або undefined). Крім того, якщо вам потрібно закрити функції членів, вам знадобляться невеликі заводи функцій для кожного з цих методів. Інша справа, що ви повинні закрити всі свої керуючі структури всередині функцій, тоді як наведена форма цього не нав'язує. Це не означає, що я не використовую OLN, це просто те, що іноді мені це не подобається.
Ionuț G. Stan

8
Мені подобається такий підхід, оскільки він дозволяє використовувати приватні функції, змінні та псевдоконстанти (тобто var API_KEY = 12345;).
Лоуренс Барсанті

12
Мені це подобається більше, ніж контейнер об'єктів, розділених комами, який проголосував вище. Я також не бачу недоліків порівняно. Я щось пропускаю?
Lucent

7
JS Новачок тут ... чому так мені не потрібно друкувати ns().publicFunction(), тобто ... ns.publicFunction()працює.
Джон Крафт

14
@John Kraft, це не через newключове слово перед functionключовим словом. В основному, це те, що він оголошує анонімну функцію (і як функцію, вона є також конструктором), а потім негайно викликає її як конструктор, використовуючи new. Таким чином, остаточне значення, яке зберігається всередині, nsє унікальним екземпляром цього анонімного конструктора. Сподіваюся, це має сенс.
Ionuț G. Stan

157

Чи є більш елегантний чи стислий спосіб зробити це?

Так. Наприклад:

var your_namespace = your_namespace || {};

тоді ти можеш мати

var your_namespace = your_namespace || {};
your_namespace.Foo = {toAlert:'test'};
your_namespace.Bar = function(arg) 
{
    alert(arg);
};
with(your_namespace)
{
   Bar(Foo.toAlert);
}

1
це дає мені помилку в IE7. var your_namespace = (typeof your_namespace == "undefined" ||! your_namespace)? {}: ваш простір імен; працює краще.
mjallday

9
це має бути var your_namespace = your_namespace = your_namespace || {} Працює в кожному браузері;)
Пало

+1 від мене! Тонкий працює як відповідь Яко Преторія, розширюючи одну бібліотеку до різних файлів або різних місць всередині одного файлу. Просто геніально!
центуріан

2
@Palo Чи можете ви поясніть, чому так має бути? var your_namespace = your_namespace = your_namespace || {}
Шрірам

6
у вас буде можливість розширити об’єкт простору_імен у різних js-файлах. Під час використання var your_namespace = {} ви не можете цього робити, оскільки об'єкт буде
переосмислений

92

Я зазвичай будую це в закритому вигляді:

var MYNS = MYNS || {};

MYNS.subns = (function() {

    function privateMethod() {
        // Do private stuff, or build internal.
        return "Message";
    }

    return {
        someProperty: 'prop value',
        publicMethod: function() {
            return privateMethod() + " stuff";
        }
    };
})();

З моменту написання мого стилю з часом відбулася тонка зміна, і зараз я вважаю, що я пишу закриття так:

var MYNS = MYNS || {};

MYNS.subns = (function() {
    var internalState = "Message";

    var privateMethod = function() {
        // Do private stuff, or build internal.
        return internalState;
    };
    var publicMethod = function() {
        return privateMethod() + " stuff";
    };

    return {
        someProperty: 'prop value',
        publicMethod: publicMethod
    };
})();

Таким чином, я вважаю, що громадський API та впровадження його легше зрозуміти. Подумайте про повернення заяви як про публічний інтерфейс для реалізації.


3
Якщо ви не перевіряєте MYNS.subns = MYNS.subns || {}??
Мирко

Хороший момент, який повинен бути вправою для намірів розробників. Вам потрібно врахувати, що робити, коли він існує, замінити його, помилку, використовувати існуючу чи перевірку версій та умовно замінити. У мене були різні ситуації, які вимагають кожного варіанта. У більшості випадків у вас це може бути виправданим випадком з низьким рівнем ризику, а заміна може бути корисною, розгляньте модуль-шахрай, який намагався викрасти НС.
Бретт Райан

1
Пояснення такого підходу є у книзі "Розмовляючий JavaScript" на сторінці 412, якщо хтось має, під заголовком "Швидкі та брудні модулі".
Соферіо

2
Порада з оптимізації: хоча var foo = functionі function fooсхожі, є приватними; завдяки динамічно набраному типу JavaScript, останній дещо швидший, оскільки він пропускає кілька інструкцій у трубопроводах більшості перекладачів. З var fooсистемою типів потрібно викликати, щоб дізнатись, який тип призначається вказаному var, тоді як при function fooцьому система типу автоматично знає, що це функція, тому пара викликів функції пропускається, що означає меншу кількість викликів інструкцій CPU, наприклад jmp, pushq, popq, і т.д., що призводить до більш короткому трубопроводу центрального процесора.
Бреден Кращий

1
@brett ой. Ти правий. Я думав про іншу мову сценаріїв. Хоча я все ще наполягаю, що function fooсинтаксис є більш читабельним. І мені ще подобається моя версія.
Бреден Кращий

56

Оскільки ви можете писати різні файли JavaScript, а пізніше комбінувати або не комбінувати їх у програмі, кожен повинен мати можливість відновити чи сконструювати об’єкт простору імен, не пошкодивши роботу інших файлів ...

Один файл може мати намір використовувати простір імен namespace.namespace1:

namespace = window.namespace || {};
namespace.namespace1 = namespace.namespace1 || {};

namespace.namespace1.doSomeThing = function(){}

Інший файл, можливо, захоче використовувати простір імен namespace.namespace2:

namespace = window.namespace || {};
namespace.namespace2 = namespace.namespace2 || {};

namespace.namespace2.doSomeThing = function(){}

Ці два файли можуть жити разом або один від одного, не стикаючись.


1
Я виявив, що це дуже корисний метод організації клієнтського скрипту в декілька файлів у великих програмах, де функціональність повинна бути модульною.
DVK

Питання з проханням спеціально для декількох файлів: stackoverflow.com/questions/5150124 / ...
Чіро Сантіллі冠状病毒审查六四事件法轮功

48

Ось як це робить Стоян Стефанов у своїй книзі " Шаблони JavaScript", яку я вважаю дуже хорошою (він також показує, як він робить коментарі, що дозволяють створювати автоматично створену документацію API, і як додати метод до прототипу спеціального об'єкта):

/**
* My JavaScript application
*
* @module myapp
*/

/** @namespace Namespace for MYAPP classes and functions. */
var MYAPP = MYAPP || {};

/**
* A maths utility
* @namespace MYAPP
* @class math_stuff
*/
MYAPP.math_stuff = {

    /**
    * Sums two numbers
    *
    * @method sum
    * @param {Number} a First number
    * @param {Number} b Second number
    * @return {Number} Sum of the inputs
    */
    sum: function (a, b) {
        return a + b;
    },

    /**
    * Multiplies two numbers
    *
    * @method multi
    * @param {Number} a First number
    * @param {Number} b Second number
    * @return {Number} The inputs multiplied
    */
    multi: function (a, b) {
        return a * b;
    }
};

/**
* Constructs Person objects
* @class Person
* @constructor
* @namespace MYAPP
* @param {String} First name
* @param {String} Last name
*/
MYAPP.Person = function (first, last) {

    /**
    * First name of the Person
    * @property first_name
    * @type String
    */
    this.first_name = first;

    /**
    * Last name of the Person
    * @property last_name
    * @type String
    */
    this.last_name = last;
};

/**
* Return Person's full name
*
* @method getName
* @return {String} First name + last name
*/
MYAPP.Person.prototype.getName = function () {
    return this.first_name + ' ' + this.last_name;
};

32

Я використовую такий підхід:

var myNamespace = {}
myNamespace._construct = function()
{
    var staticVariable = "This is available to all functions created here"

    function MyClass()
    {
       // Depending on the class, we may build all the classes here
       this.publicMethod = function()
       {
          //Do stuff
       }
    }

    // Alternatively, we may use a prototype.
    MyClass.prototype.altPublicMethod = function()
    {
        //Do stuff
    }

    function privateStuff()
    {
    }

    function publicStuff()
    {
       // Code that may call other public and private functions
    }

    // List of things to place publically
    this.publicStuff = publicStuff
    this.MyClass = MyClass
}
myNamespace._construct()

// The following may or may not be in another file
myNamespace.subName = {}
myNamespace.subName._construct = function()
{
   // Build namespace
}
myNamespace.subName._construct()

Зовнішнім кодом може бути:

var myClass = new myNamespace.MyClass();
var myOtherClass = new myNamepace.subName.SomeOtherClass();
myNamespace.subName.publicOtherStuff(someParameter);

Чудова деталь! Дякую! Цікаво, що ви берете на Namespace.js. Я ніколи не використовував його сам, тому мені цікаво, чи хтось із вашими знаннями / вмінням / досвідом вирішив би його використовувати.
Джон

Мені це подобається! З іншого боку, я отримую виняток у першому рядку цього зовнішнього коду, кажучи: 'myNameSpace.MyClass' [undefined] - не конструктор. можливо, це залежить від впровадження JS? : /
yoosiba

@yossiba: Можливо. Код, наведений вище - досить стандартний матеріал. У стандартному JS будь-яка функція може бути використана як конструктор, нічого не потрібно робити, щоб позначити функцію як специфічну для використання в якості конструктора. Ви використовуєте незвичайний аромат, як ActionScript, чи щось таке?
AnthonyWJones

@Anthony краще використовувати var MYNAMESPACE = MYNAMESPACE || {}; просто використання var myNamespace = {} небезпечно, і тим більше, що краще оголосити простір імен великими літерами
Павло

9
@paul: "Краще" може бути досить суб'єктивним. Я ненавиджу код читання, який ВИНАГАЄ на мене, тому я уникаю використання ідентифікаторів, які використовують всі великі регістри. Хоча це ns = ns || {}може здатися більш захисним, це може призвести до інших несподіваних результатів.
AnthonyWJones

32

Це подальше посилання на користувач106826 на Namespace.js. Здається, проект перейшов до GitHub . Зараз це коваль / іменаpacedotjs .

Я використовую цей простий помічник JavaScript для мого крихітного проекту, і поки що він здається легким, але досить універсальним для обробки простору імен і завантаження модулів / класів. Було б чудово, якби це дозволило мені імпортувати пакет у простір імен на мій вибір, а не лише у глобальний простір імен ... зітхніть, але це крім суті.

Це дозволяє оголосити простір імен, а потім визначити об'єкти / модулі в цьому просторі імен:

Namespace('my.awesome.package');
my.awesome.package.WildClass = {};

Ще один варіант - оголосити простір імен та його вміст одразу:

Namespace('my.awesome.package', {
    SuperDuperClass: {
        saveTheDay: function() {
            alert('You are welcome.');
        }
    }
});

Більше прикладів використання дивіться у файлі example.js у джерелі .


2
Поки ви пам’ятаєте, це має певні наслідки для продуктивності, оскільки кожен раз, коли ви отримуєте доступ до my.awesome.package.WildClass, ви отримуєте доступ до дивовижної властивості мого, властивості пакета my.awesome та властивості WildClass my.awesome. пакет.
SamStephens

29

Зразок:

var namespace = {};
namespace.module1 = (function(){

    var self = {};
    self.initialized = false;

    self.init = function(){
        setTimeout(self.onTimeout, 1000)
    };

    self.onTimeout = function(){
        alert('onTimeout')
        self.initialized = true;
    };

    self.init(); /* If it needs to auto-initialize, */
    /* You can also call 'namespace.module1.init();' from outside the module. */
    return self;
})()

Ви можете за бажанням оголосити localзмінну, sameяк, наприклад, selfі призначити, local.onTimeoutякщо ви хочете, щоб вона була приватною.


14

Ви можете оголосити просту функцію для надання просторів імен.

function namespace(namespace) {
    var object = this, tokens = namespace.split("."), token;

    while (tokens.length > 0) {
        token = tokens.shift();

        if (typeof object[token] === "undefined") {
            object[token] = {};
        }

        object = object[token];
    }

    return object;
}

// Usage example
namespace("foo.bar").baz = "I'm a value!";

13

Якщо вам потрібна приватна сфера:

var yourNamespace = (function() {

  //Private property
  var publicScope = {};

  //Private property
  var privateProperty = "aaa"; 

  //Public property
  publicScope.publicProperty = "bbb";

  //Public method
  publicScope.publicMethod = function() {
    this.privateMethod();
  };

  //Private method
  function privateMethod() {
    console.log(this.privateProperty);
  }

  //Return only the public parts
  return publicScope;
}());

yourNamespace.publicMethod();

інакше, якщо ви ніколи не будете використовувати приватну область:

var yourNamespace = {};

yourNamespace.publicMethod = function() {
    // Do something...
};

yourNamespace.publicMethod2 = function() {
    // Do something...
};

yourNamespace.publicMethod();

12

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

Працюючи з шаблоном Модуль, нам може бути корисно визначити простий шаблон, який ми використовуємо для початку роботи з ним. Ось один, який охоплює проміжні імена, публічні та приватні змінні.

У JavaScript модель Модуль використовується для подальшої емуляції концепції класів таким чином, що ми маємо змогу включати як публічні / приватні методи, так і змінні всередині одного об’єкта, тим самим захищаючи окремі частини від глобальної сфери. Це призводить до зменшення ймовірності того, що назви наших функцій суперечать іншим функціям, визначеним додатковими сценаріями на сторінці.

var myNamespace = (function () {

  var myPrivateVar, myPrivateMethod;

  // A private counter variable
  myPrivateVar = 0;

  // A private function which logs any arguments
  myPrivateMethod = function( foo ) {
      console.log( foo );
  };

  return {

    // A public variable
    myPublicVar: "foo",

    // A public function utilizing privates
    myPublicFunction: function( bar ) {

      // Increment our private counter
      myPrivateVar++;

      // Call our private method using bar
      myPrivateMethod( bar );

    }
  };

})();

Переваги

Чому модель Модуль є хорошим вибором? Для початку це набагато чистіше для розробників, що виходять з об'єктно-орієнтованого фону, ніж ідеї справжнього капсулювання, принаймні з точки зору JavaScript.

По-друге, він підтримує приватні дані - так, у моделі Module, публічні частини нашого коду можуть торкатися приватних частин, однак зовнішній світ не в змозі торкнутися приватних частин класу.

Недоліки

Недоліками моделі модуля є те, що, коли ми отримуємо доступ до публічних та приватних членів по-різному, коли ми хочемо змінити видимість, ми насправді мусимо внести зміни до кожного місця, в якому користувач використовувався.

Ми також не можемо отримати доступ до приватних членів методами, які додаються до об'єкта пізніше . Зважаючи на це, у багатьох випадках модель Модуль все ще є досить корисною і при правильному використанні, безумовно, має потенціал для покращення структури нашого додатку.

Шаблон модуля розкриття

Тепер, коли ми трохи більше знайомі з схемою модуля, давайте подивимося на трохи вдосконалену версію - модель виявлення модуля Крістіана Хайльмана.

Модель модуля розкриття виникла, коли Хайльман був розчарований тим, що йому довелося повторювати ім'я головного об'єкта, коли ми хотіли викликати один відкритий метод з іншого або отримувати доступ до загальнодоступних змінних. заперечувати буквальне позначення речей, які він хотів оприлюднити.

Результатом його зусиль стала оновлена ​​модель, де ми просто визначили всі наші функції та змінні у приватному масштабі та повернемо анонімний об’єкт із вказівниками до приватної функціональності, яку ми хотіли розкрити як загальнодоступну.

Приклад використання схеми модуля розкриття можна знайти нижче

var myRevealingModule = (function () {

        var privateVar = "Ben Cherry",
            publicVar = "Hey there!";

        function privateFunction() {
            console.log( "Name:" + privateVar );
        }

        function publicSetName( strName ) {
            privateVar = strName;
        }

        function publicGetName() {
            privateFunction();
        }


        // Reveal public pointers to
        // private functions and properties

        return {
            setName: publicSetName,
            greeting: publicVar,
            getName: publicGetName
        };

    })();

myRevealingModule.setName( "Paul Kinlan" );

Переваги

Цей візерунок дозволяє синтаксису наших сценаріїв бути більш послідовним. Це також робить більш зрозумілим в кінці модуля, до яких наших функцій та змінних може бути відкрито доступ, що полегшує читабельність.

Недоліки

Недоліком цієї шаблону є те, що якщо приватна функція посилається на публічну функцію, цю публічну функцію неможливо змінити, якщо необхідний патч. Це пояснюється тим, що приватна функція продовжуватиме посилатися на приватну реалізацію, а модель не поширюється на публічних членів, а лише на функції.

Члени публічних об'єктів, які посилаються на приватні змінні, також підпадають під дію вищезазначених правил без патчів.


9

Я створив простір імен який надихається модулями Ерланга. Це дуже функціональний підхід, але саме так я пишу свій JavaScript код сьогодні.

Це дає закриття глобального простору імен і розкриває визначені набори функцій в рамках цього закриття.

(function(){

  namespace("images", previous, next);
  // ^^ This creates or finds a root object, images, and binds the two functions to it.
  // It works even though those functions are not yet defined.

  function previous(){ ... }

  function next(){ ... }

  function find(){ ... } // A private function

})();

8

Після перенесення декількох моїх бібліотек до різних проектів і постійного змінення простору імен верхнього рівня (статично названих) я перейшов до використання цієї невеликої допоміжної функції (з відкритим кодом) для визначення просторів імен.

global_namespace.Define('startpad.base', function(ns) {
    var Other = ns.Import('startpad.other');
    ....
});

Опис переваг - у моєму дописі на блозі . Ви можете взяти тут вихідний код .

Однією з переваг, що мені дуже подобається, є ізоляція між модулями щодо порядку завантаження. Ви можете звернутися до зовнішнього модуля перед тим, як він завантажений. І отримана вами посилання на об'єкт буде заповнена, коли код буде доступний.


1
Я створив вдосконалену версію (2.0) бібліотеки простору імен: code.google.com/p/pageforest/source/browse/appengine/static/src/…
mckoss

всі ваші посилання здаються мертвими
snoob dogg

8

Я використовую наступний синтаксис для простору імен.

var MYNamespace = MYNamespace|| {};

 MYNamespace.MyFirstClass = function (val) {
        this.value = val;
        this.getValue = function(){
                          return this.value;
                       };
    }

var myFirstInstance = new MYNamespace.MyFirstClass(46);
alert(myFirstInstance.getValue());

jsfiddle: http://jsfiddle.net/rpaul/4dngxwb3/1/


8

Я спізнююсь на 7 років на вечірку, але 8 років тому зробив досить багато роботи:

Важливо вміти легко та ефективно створювати декілька вкладених просторів імен, щоб зберегти організований і керований складний веб-додаток, дотримуючись глобального простору імен JavaScript (запобігання забрудненню простору імен), і не клобувати жодних існуючих об’єктів у шляху простору імен, виконуючи це. .

З усього вищесказаного, це було моїм рішенням близько 2008 року:

var namespace = function(name, separator, container){
  var ns = name.split(separator || '.'),
    o = container || window,
    i,
    len;
  for(i = 0, len = ns.length; i < len; i++){
    o = o[ns[i]] = o[ns[i]] || {};
  }
  return o;
};

Це не створює простір імен, але забезпечує функцію створення просторів імен.

Це може бути конденсовано до мінімізованого однопластового:

var namespace=function(c,f,b){var e=c.split(f||"."),g=b||window,d,a;for(d=0,a=e.length;d<a;d++){g=g[e[d]]=g[e[d]]||{}}return g};

Приклад використання:

namespace("com.example.namespace");
com.example.namespace.test = function(){
  alert("In namespaced function.");
};

Або, як одне твердження:

namespace("com.example.namespace").test = function(){
  alert("In namespaced function.");
};

Або виконується як:

com.example.namespace.test();

Якщо вам не потрібна підтримка застарілих браузерів, оновлена ​​версія:

const namespace = function(name, separator, container){
    var o = container || window;
    name.split(separator || '.').forEach(function(x){
        o = o[x] = o[x] || {};
    });
    return o;
};

Тепер я б хотів розкрити namespaceсам глобальний простір імен. (Шкода, що основна мова не надає цього для нас!) Тому я зазвичай використовую це сам у закритті, наприклад:

(function(){
	const namespace = function(name, separator, container){
		var o = container || window;
		name.split(separator || '.').forEach(function(x){
			o = o[x] = o[x] || {};
		});
		return o;
	};
	const ns = namespace("com.ziesemer.myApp");
	
	// Optional:
	ns.namespace = ns;
	
	// Further extend, work with ns from here...
}());

console.log("\"com\":", com);

Для більшої програми це потрібно визначити лише один раз на початку завантаження сторінки (для клієнтських веб-додатків). Потім додаткові файли можуть повторно використовувати функцію простору імен, якщо вона зберігається (включена як "необов'язкова" у вище). У гіршому випадку, якщо цю функцію повторно оголошувати кілька разів - це лише кілька рядків коду, і менше, якщо їх мінімізувати.


2

Мені подобається рішення Яко Преторіуса, але я хотів зробити ключове слово "це" трохи кориснішим, вказавши його на об'єкт модуля / простору імен. Моя версія сковороди:

(function ($, undefined) {

    console.log(this);

}).call(window.myNamespace = window.myNamespace || {}, jQuery);

2

Моїм улюбленим малюнком останнім часом став такий:

var namespace = (function() {
  
  // expose to public
  return {
    a: internalA,
    c: internalC
  }

  // all private
  
  /**
   * Full JSDoc
   */
  function internalA() {
    // ...
  }
  
  /**
   * Full JSDoc
   */
  function internalB() {
    // ...
  }
  
  /**
   * Full JSDoc
   */
  function internalC() {
    // ...
  }
  
  /**
   * Full JSDoc
   */
  function internalD() {
    // ...
  }
  
})();

Звичайно, повернення може бути наприкінці, але якщо за ним слідують лише декларації функцій, набагато простіше зрозуміти, про що йдеться в просторі імен та на який API.

Шаблон використання функціональних виразів у таких випадках призводить до того, що ми не можемо знати, які методи піддаються впливу, не переглядаючи весь код.


Привіт, як ви називаєте публічні функції зі свого фрагмента? Я спробувавnamespace.a();
olimart

@olivier так, це ідея. Хоча зараз із ES6, я зазвичай використовую стенографічний синтаксис об'єктних літералів ( ponyfoo.com/articles/es6-object-literal-features-in-depth )
Іменований

2

Я думаю, що ви всі використовуєте занадто багато коду для такої простої проблеми. Для цього не потрібно робити репо. Ось функція одного рядка.

namespace => namespace.split(".").reduce((last, next) => (last[next] = (last[next] || {})), window);

Спробуй це :

// --- definition ---
const namespace = namespace => namespace.split(".").reduce((last, next) => (last[next] = (last[next] || {})), window);

// --- Use ----
let myNamespace = namespace("a.b.c");
myNamespace.MyClass = class MyClass {};

// --- see ----
console.log("a : ", a);


1

Якщо ви використовуєте Makefile, ви можете це зробити.

// prelude.hjs
billy = new (
    function moduleWrapper () {
    const exports = this;

// postlude.hjs
return exports;
})();

// someinternalfile.js
function bob () { console.log('hi'); }
exports.bob = bob;

// clientfile.js
billy.bob();

Я вважаю за краще використовувати Makefile так чи інакше, коли я перейду до приблизно 1000 рядків, оскільки можу ефективно коментувати великі пробіли коду, видаляючи один рядок у makefile. Це дозволяє легко пограбувати з речами. Крім того, за допомогою цієї техніки простір імен з’являється лише один раз у прелюдії, тому її легко змінити, і вам не доведеться повторювати це всередині коду бібліотеки.

Сценарій оболонки для прямої розробки у браузері при використанні makefile:

while (true); do make; sleep 1; done

Додайте це як завдання зробити "йти", і ви можете "зробити перехід", щоб оновити збірку під час кодування.


1

Досить відповідь на відповідь Іону Ґ. Стен, але показує переваги незатисненого коду за допомогою var ClassFirst = this.ClassFirst = function() {...}, який використовує перевагу показника закриття JavaScript для меншого захаращення простору імен для класів в одному просторі імен.

var Namespace = new function() {
    var ClassFirst = this.ClassFirst = function() {
        this.abc = 123;
    }

    var ClassSecond = this.ClassSecond = function() {
        console.log("Cluttered way to access another class in namespace: ", new Namespace.ClassFirst().abc);
        console.log("Nicer way to access a class in same namespace: ", new ClassFirst().abc);
    }
}

var Namespace2 = new function() {
    var ClassFirst = this.ClassFirst = function() {
        this.abc = 666;
    }

    var ClassSecond = this.ClassSecond = function() {
        console.log("Cluttered way to access another class in namespace: ", new Namespace2.ClassFirst().abc);
        console.log("Nicer way to access a class in same namespace: ", new ClassFirst().abc);
    }
}

new Namespace.ClassSecond()
new Namespace2.ClassSecond()

Вихід:

Cluttered way to access another class in namespace: 123
Nicer way to access a class in same namespace: 123
Cluttered way to access another class in namespace: 666
Nicer way to access a class in same namespace: 666

1

Я написав ще одну бібліотеку простору імен, яка працює трохи більше, як пакунки / блоки на інших мовах. Це дозволяє створити пакет коду JavaScript і посилання на цей пакет з іншого коду:

Файл hello.js

Package("hello", [], function() {
  function greeting() {
    alert("Hello World!");
  }
  // Expose function greeting to other packages
  Export("greeting", greeting);
});

Файл Example.js

Package("example", ["hello"], function(greeting) {
  // Greeting is available here
  greeting();  // Alerts: "Hello World!"
});

На сторінці потрібно включити лише другий файл. Її залежності (файл hello.js у цьому прикладі) автоматично завантажуються, а об'єкти, експортовані з цих залежностей, будуть використані для заповнення аргументів функції зворотного виклику.

Ви можете знайти відповідний проект у пакетах JS .


1
@ peter-mortensen Чи справді потрібні зміни в моїй відповіді від 11 року? Це точно не вандалізм, що ти робиш, не зрозумій мене, але вони дуже поверхневі. Я вважаю за краще залишатися єдиним автором таких публікацій, якщо ви дійсно не додасте щось хороше.
Штійн де Вітт

1

Ми можемо використовувати його самостійно таким чином:

var A = A|| {};
A.B = {};

A.B = {
    itemOne: null,
    itemTwo: null,
};

A.B.itemOne = function () {
    //..
}

A.B.itemTwo = function () {
    //..
}

0

Моя звичка - використовувати функцію myName () як сховище власності, а потім var myName як власник "методу" ...

Будь це досить законно чи ні, побийте мене! Я весь час покладаюся на свою логіку PHP, і все просто працює. : D

function myObj() {
    this.prop1 = 1;
    this.prop2 = 2;
    this.prop3 = 'string';
}

var myObj = (
 (myObj instanceof Function !== false)
 ? Object.create({

     $props: new myObj(),
     fName1: function() { /* code..  */ },
     fName2: function() { /* code ...*/ }
 })
 : console.log('Object creation failed!')
);

if (this !== that) myObj.fName1(); else myObj.fName2();

Ви також можете це зробити "навпаки", щоб перевірити перед створенням об'єкта, що набагато краще :

function myObj() {
    this.prop1 = 1;
    this.prop2 = 2;
    this.prop3 = 'string';
}

var myObj = (
    (typeof(myObj) !== "function" || myObj instanceof Function === false)
    ? new Boolean()
    : Object.create({
        $props: new myObj(),
        init: function () { return; },
        fName1: function() { /* code..  */ },
        fName2: function() { /* code ...*/ }
    })
);

if (myObj instanceof Boolean) {
    Object.freeze(myObj);
    console.log('myObj failed!');
    debugger;
}
else
    myObj.init();

Посилання на це: JavaScript: Створення об’єкта за допомогою Object.create ()


0

У JavaScript немає заздалегідь визначених методів використання просторів імен. У JavaScript ми повинні створити власні методи для визначення NameSpaces. Ось процедура, яку ми дотримуємося в технологіях Oodles.

Реєстрація простору імен Далі йде функція реєстрації простору імен

//Register NameSpaces Function
function registerNS(args){
 var nameSpaceParts = args.split(".");
 var root = window;

 for(var i=0; i < nameSpaceParts.length; i++)
 {
  if(typeof root[nameSpaceParts[i]] == "undefined")
   root[nameSpaceParts[i]] = new Object();

  root = root[nameSpaceParts[i]];
 }
}

Щоб зареєструвати простір імен, просто зателефонуйте до вищевказаної функції з аргументом як простір імен, розділений '.'(крапка). Наприклад, Нехай назва вашої програми - oodles. Ви можете створити простір імен наступним способом

registerNS("oodles.HomeUtilities");
registerNS("oodles.GlobalUtilities");
var $OHU = oodles.HomeUtilities;
var $OGU = oodles.GlobalUtilities;

В основному це створить структуру NameSpaces, як показано нижче, у бекенді:

var oodles = {
    "HomeUtilities": {},
    "GlobalUtilities": {}
};

У наведеній вище функції ви зареєструєте простір імен під назвою "oodles.HomeUtilities"та "oodles.GlobalUtilities". Для виклику цих просторів імен ми робимо змінну, тобто var $OHUта var $OGU.

Ці змінні є не що інше, як псевдонім Intialization простору імен. Тепер, коли ви оголошуєте функцію, що належить HomeUtilitiesвам, декларуйте її так:

$OHU.initialization = function(){
    //Your Code Here
};

Вище - ініціалізація імені функції, і вона розміщується в просторі імен $OHU. і викликати цю функцію в будь-якому місці файлів сценарію. Просто використовуйте наступний код.

$OHU.initialization();

Так само і з іншими NameSpaces.

Сподіваюся, це допомагає.


0

JavaScript не підтримує простір імен за замовчуванням. Отже, якщо ви створюєте будь-який елемент (функцію, метод, об’єкт, змінну), він стає глобальним і забруднює глобальний простір імен. Візьмемо приклад визначення двох функцій без простору імен,

function func1() {
    console.log("This is a first definition");

}
function func1() {
    console.log("This is a second definition");
}
func1(); // This is a second definition

Він завжди викликає визначення другої функції. У цьому випадку простір імен вирішить проблему зіткнення імен.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.