2012-11-28 10 views
21

Piszę bibliotekę. Teraz mam wszystko napisane w jednym pliku .js że idzie tak:Pisanie biblioteki, jaka struktura?

function doThis() {} 
var thisVar = 5; 

nie byłem pewien, czy to w porządku, więc uznałem:

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

var lib = new Lib(); 

Następnie w programie głównym pliki, musiałbym nazwać wszystko za pomocą "lib.", na przykład "lib.thisVar" lub "lib.doThis();".

Jakieś pomysły, które byłyby lepsze, a może oba są dopuszczalne? Z góry dziękuję.

+0

Myślę, że powinieneś po prostu popatrzeć, jak robią to inne biblioteki javascript, takie jak prototyp jQuery oder itd. – Ridcully

+1

Sprawdź wzór modułu: http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Dthth – edigu

+0

Czy można poprawnie używać 'var lib = new function() {/ * blah blah * /} '? – Pithikos

Odpowiedz

6

Obie są do przyjęcia teoretycznie. Oba jednak ryzykują nazywanie kolizji z innymi częściami/bibliotekami używanymi w aplikacji.

W pierwszym przypadku istnieje ryzyko nazwania kolizji dla poszczególnych funkcji, natomiast w tym drugim przypadku istnieje ryzyko nazwania kolizji dla funkcji wybranej dla opakowania biblioteki (Lib).

Preferowaną metodą byłoby zawinąć je w osobnej przestrzeni nazw, jak pokazano na tej stronie:

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

0

To ostatnie jest lepsze. Szczególnie przy pisaniu bibliotek ważne jest unikanie zanieczyszczania globalnej przestrzeni nazw w jak największym stopniu. Twój kod musi jak najmniej kolidować z istniejącym kodem.

3

Tak, to ostatnie podejście jest lepsze, gdyż nie stwarza wiele zmiennych globalnych ("Global zakres zanieczyszczeń "), ale tworzy przestrzenie nazw na obiekcie.

Jednak nie ma potrzeby konstruktora (Lib), można utworzyć instancję lib tylko raz (zgodnie ze wzorcem singleton); i nie potrzebujesz prototypu. Zamiast używać prostych literał obiektu:

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

dla prywatnych (choć statyczne) zmiennych oraz lepszą organizację kodu, również spojrzeć na module pattern (lub here):

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

Aby uniknąć zaśmiecania globalnej przestrzeni nazw, używam strukturę tak:

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

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

Zauważysz umieściłem wszystkie zmienne do własnej sub-object, odbywa się to wyłącznie do łatwe czytanie i rozwój.


EDIT 1:

Oto kolejny metoda używam

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()); 

Struktura ta wykorzystuje JS Closures i funkcją konstruktora, więc jej łatwe do utrzymania w zmiennych prywatnych prywatne.

EDIT 2:

Ponadto, jakie stosuje się również inne ustawienia zamknięcia, które nie zwraca konstruktor (np 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 : {})); 

I aby zobaczyć go w akcji:

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

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

Proszę również spojrzeć na drodze ja wystawiając myLib, biorąc pod uwagę, gdzie jest prowadzony i jak to jest włączone.

EDIT 3 (7/2017):

Jako pełnoprawny stosie (w/node.js) inżynier JavaScript, a nadejście Browserify, w pełni polecam korzystanie z Nodejs stylu module wzór wykorzystanie Gulpa lub Grunta jako systemu kompilacji do kompilowania wielu plików (odsprzęgniętych, mniejszych bitów kodu) do jednej biblioteki.

Model ten zachęca do bardziej funkcjonalnego podejścia, pozwalając programistom na upowszechnienie bardziej popularnych funkcji w bibliotece w osobnych plikach, co znacznie ułatwia rozwój.

Och, i użyj ES6!

// file: src/divideIntByFour.js 

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

module.exports = divideIntByFour; 

... Jako siplified przykład

-1

Drugie podejście jest zalecane praktyki, ale że można poprawić poprzez stworzenie nowej przestrzeni nazw. Zaletą jest to, że możesz uniknąć kolizji nazw, które mogą wystąpić, a także będziesz mieć lepszą kontrolę nad swoim kodem. Utworzyłem przestrzeń nazw o nazwie "MyNamespace" i przechowuję w niej swoją bibliotekę. W ten sposób, nawet jeśli istnieje inny plik, który ma nazwę klasy "Biblioteka", nie będzie kolizji nazwy.

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

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

    }, 
    function2 : function() { 

    } 
}; 

})(MyNameSpace); 

Za pomocą poniższego kodu można utworzyć nowe obiekty dla klasy Biblioteka.

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

Post jest stary, ale może moje 2 centy mogą pomóc. Widziałem i zrobił to z wieloma bibliotekami:

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; 
} 
0

UMD co jest generalnie używany.

(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 

To Iife który jest natychmiast wywoływane z 2 argumentami: this i ekspresja funkcji, które są parametry global i factory odpowiednio. Dzięki temu zakres implementacji jest prywatny i zapobiega kolizjom nazw.

Jeśli skrypt jest uruchamiany w przeglądarce, this będzie window, więc window.MyLibrary będzie równa factory() który jest cokolwiek kończy się wrócił z wyrażeniem funkcyjnym w drugim argumencie. To jest interfejs API biblioteki.

Powiązane problemy