Подтвердить что ты не робот

Написание библиотеки, какая структура?

Я пишу библиотеку. Прямо сейчас, у меня есть все, написанное в одном файле .js, который выглядит как:

function doThis() {}
var thisVar = 5;

Я не был уверен, правильно ли это, поэтому я подумал:

function Lib() {
  this.doThis = function() {}
  this.thisVar = 5;
}

var lib = new Lib();

Затем, в основных файлах программы, мне нужно было бы вызвать все с помощью "lib", например "lib.thisVar" или "lib.doThis();".

Любые идеи, по которым было бы лучше, или они оба приемлемы? Заранее благодарю вас.

4b9b3361

Ответ 1

Оба приемлемы теоретически. Но оба риска могут называть конфликты с другими частями/библиотеками, используемыми в вашем приложении.

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

Предпочтительным методом было бы обернуть их в отдельное пространство имен, как показано на этой странице:

http://frugalcoder.us/post/2010/02/11/js-classes.aspx

Ответ 2

Чтобы избежать загромождения глобального пространства имен, я использую такую ​​структуру:

var MyLib = {
    vars: {
        var1: 'value1',
        var2: 'value2'
    },
    func1: function () {
        return this.vars.var1;
    },
    func2: function () {
        alert("This is func2");
    }
};

MyLib.func1();
MyLib.func2();

Вы заметите, что я поместил все переменные в свой собственный под-объект, это делается исключительно для удобства чтения и разработки.


РЕДАКТИРОВАТЬ 1:

Вот еще один метод, который я использую

var MyLib = (function MyLib() {
    var _privateVars = {
        "someVar": "This value made public by `someMethod`",
        "privateVar": "Can't see this value"
    };

    // Return the constructor
    return function MyLibConstructor() {
        var _this = this; // Cache the `this` keyword

        _this.someMethod = function () {
            // Access a private variable
            return _privateVars.someVar;
        };

        _this.someOtherMethod = function () {
            // Some other functionality
        };
    };
}());

var myLib = new MyLib(); // invoke

console.log( myLib.someMethod() );

В этой структуре используется JS Closures и функция конструктора, поэтому ее легко сохранить частные переменные private.

ИЗМЕНИТЬ 2:

Кроме того, я также использовал другую настройку закрытия, которая не возвращает конструктор (например, var x = new MyLib();).

(function(window) {
    var _private = {},
        methods = {},
        topic, init;

    methods.set = function(value) {
        // Set the property & value
        _private[topic] = value;
        return this;
    };

    // A simple get method
    methods.get = function(callback) {
        var response = null;

        // Return the value of topic (property) in a callback
        if (!!callback && typeof callback === 'function') {
            if (_private.hasOwnProperty(topic)) {
                response = _private[topic];
            }
            callback.call(this, response);
        }
        return this;
    };

    // Init method setting the topic and returning the methods.
    init = function(_topic) {
        topic = _topic;

        return methods;
    };

    // Exposure when being used in an AMD environment, e.g. RequireJS
    if (typeof define === 'function' && define.amd) {
        define(function() {
            return init;
        });
        return;
    }

    // Exposure when being used with NodeJS
    if ('undefined' !== typeof module && module.exports) {
        module.exports = init;
        return;
    }

    // Last-in-the-line exposure to the window, if it exists
    window.myLib = init;

    // This line either passes the `window` as an argument or
    // an empty Object-literal if `window` is not defined.
}(('undefined' !== typeof window) ? window : {}));

И чтобы увидеть это в действии:

myLib('something').set('made public, outside of the closure by the `get` method');

myLib('something').get(function(a){
  console.log(a);
});

Также обратите внимание на то, как я показываю myLib, принимая во внимание, где он выполняется, и как он включается.

РЕДАКТИРОВАТЬ 3 (7/2017):

В качестве полного стека (w/ Node.js) JavaScript-инженера и появления Browserify я полностью рекомендую использовать шаблон стиля module в стиле Nodejs, используя либо Gulp, либо Grunt как систему сборки для компиляции нескольких файлов (развязанных, меньших бит кода) в одну библиотеку.

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

О, и используйте ES6!

// file: src/divideIntByFour.js

const divideIntByFour = (int) => {
    return int / 4;
};

module.exports = divideIntByFour;

... В качестве обобщенного примера

Ответ 4

Да, последний подход лучше, поскольку он не создает много глобальных переменных ( "глобальное загрязнение границ" ), а namespaces их на объекте.

Тем не менее, нет необходимости в конструкторе (Lib), вы должны создать экземпляр Lib только один раз (после шаблона singleton); и вам не нужен прототип. Вместо этого используйте простой литерал объекта:

var lib = {
    doThis: function() {
    },
    thisVar: 5
};

Для частных (хотя и статических) переменных и лучшей организации кода также рассмотрите шаблон модуля (или здесь):

var lib = (function(){
    var thisVar = 5;
    function doThis() {}
    return { // "export"
        doThat: doThis
    };
})();

Ответ 5

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

Ответ 6

Сообщение старое, но, может быть, мои 2 цента могут помочь. Я видел и делал это с несколькими библиотеками:

nameOfLibrary = function() { //name of library needs to be unique enough not to conflict with anyones code
  nameOfLibrary = {};

  //you can declare variables or functions only visible to this scope anywhere here
  var randomVar = 'testVar';
  function onlyVisibleHere() { /* doSomething */ }

  //if you want functions visible to outside set them like so 
  nameOfLibrary.nameOfFunction = function() {
    //code to run
  }

  //lastly return your library
  return nameOfLibrary;
}

Ответ 7

UMD обычно используется.

(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
    (global.MyLibrary = factory());
}(this, (function () { 'use strict';

  // Private implementation here

  let secret = "message";

  // Whatever is returned is what `global.MyLibrary` will be
  return {
     getSecret: () => secret
  };

})));

console.log(MyLibrary.getSecret()); // "message"
console.log(secret); // Uncaught ReferenceError: secret is not defined

Это IIFE, который немедленно вызывается с двумя аргументами: this и выражение функции, которые являются параметрами global и factory соответственно. Это позволяет ограничить объем вашей реализации и предотвращает конфликты имен.

Если script запущен в браузере, this будет window, поэтому window.MyLibrary будет равно factory(), который в конечном итоге возвращается из выражения функции во втором аргументе. Это API-интерфейс библиотеки.

Ответ 8

Второй подход - рекомендуемая практика, но это можно улучшить, создав новое пространство имен. Преимущество заключается в том, что вы сможете избежать всех коллизий имен, которые могут возникнуть, плюс вы получите лучший контроль над своим кодом. Я создал пространство имен, называемое "MyNamespace", и я храню в нем свой класс библиотеки. Таким образом, даже если есть другой файл с именем класса "Библиотека", конфликт имен не будет.

(function($){
    $.Library = function(){
        this.memory = {}; 
        this.doSomething  = function() {};
    };

$.Library.prototype = { //these will be common for all the objects 
    commonFunction : function() {

    },
    function2 : function() {

    }
};

})(MyNameSpace);

Используя приведенный ниже код, вы можете создавать новые объекты для класса Library.

<script>
var lib = new MyNamespace.Library;
</script>