Làm cách nào để khai báo một không gian tên trong JavaScript?


990

Làm cách nào để tạo một không gian tên trong JavaScript để các đối tượng và hàm của tôi không bị ghi đè bởi các đối tượng và hàm cùng tên khác? Tôi đã sử dụng như sau:

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

Có một cách thanh lịch hoặc cô đọng hơn để làm điều này?


20
Tôi có thể thấy bạn đang đi đâu để kiểm tra xem có phải không gian tên được lấy không, nhưng vì đối tượng sẽ không được tạo nếu thất bại này tôi nghĩ cách tiếp cận tốt hơn là cảnh báo nếu không gian tên được thực hiện. Thành thật mà nói điều này không nên xảy ra trong hầu hết các tình huống JS và nên được phát hiện nhanh chóng.
annakata

18
Lấy một "không gian tên" cấp cao nhất (thuộc tính cửa sổ). Sở hữu nó. Xung đột nên được phát hiện sớm trong thử nghiệm. Đừng bận tâm thêm tất cả các kiểm tra "nếu như". Đây là một vấn đề nghiêm trọng đối với "không gian tên" trùng lặp và nên được xử lý như vậy . Bạn có thể làm theo cách tiếp cận như jQuery để cho phép sinh sống một "không gian tên" tùy chỉnh; nhưng đây vẫn là một vấn đề thời gian thiết kế.

Vui lòng thay đổi câu trả lời được chấp nhận của bạn thành stackoverflow.com/questions/881515/ , đây là một giải pháp cập nhật và thanh lịch hơn nhiều.
hlfcoding

@pst những gì về YUI làm gì? Tôi tin rằng họ làm chính xác điều này để tăng dần vào không gian tên của họ. Những thủ thuật như thế này có cần thiết cho hiệu năng trong môi trường HTTP không?
Simon_Weaver

xem thêm stackoverflow.com/questions/2102591/ cho các vấn đề về hiệu suất
Tim Abell

Câu trả lời:


764

Tôi thích điều này:

var yourNamespace = {

    foo: function() {
    },

    bar: function() {
    }
};

...

yourNamespace.foo();

62
Điểm quan trọng là tôn giáo về việc mở rộng không hơn một biến gốc. Tất cả mọi thứ phải chảy từ đây.
annakata

22
Điều này không tạo ra một bao đóng cho mã của bạn - nó khiến cho việc gọi các hàm khác của bạn trở nên tẻ nhạt vì chúng luôn phải trông giống như: yourNamespace.bar (); Tôi đã thực hiện một dự án nguồn mở CHỈ để giải quyết vấn đề thiết kế này: github.com/mckoss/namespace .
mckoss

24
annakata: "Điểm quan trọng là tôn giáo về việc mở rộng không xa hơn một biến gốc." - Tại sao lại như vậy?
dùng406905

11
@alex - Tại sao nên có cấu trúc đối tượng nông?
Ryan

25
@Ryan Ý tôi là mọi thứ nên ở dưới MyApp, ví dụ MyApp.Views.Profile = {}chứ không phải MyApp.users = {}MyViews.Profile = {}. Không nhất thiết là chỉ nên có hai cấp độ sâu.
alex

1042

Tôi sử dụng cách tiếp cận được tìm thấy trên trang web Enterprise jQuery :

Dưới đây là ví dụ của họ cho thấy cách khai báo các thuộc tính và chức năng riêng tư & công cộng. Tất cả mọi thứ được thực hiện như là một chức năng ẩn danh tự thực hiện.

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

Vì vậy, nếu bạn muốn truy cập một trong những thành viên công cộng, bạn sẽ chỉ đi skillet.fry()hoặcskillet.ingredients .

Điều thực sự thú vị là bây giờ bạn có thể mở rộng không gian tên bằng cách sử dụng cú pháp chính xác tương tự.

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

Đối undefinedsố thứ ba

Đối số thứ ba, undefinedlà nguồn của biến giá trị undefined. Tôi không chắc liệu nó có còn phù hợp ngày hôm nay không, nhưng trong khi làm việc với các trình duyệt / tiêu chuẩn JavaScript cũ hơn (ecmascript 5, javascript <1.8.5 ~ firefox 4), biến phạm vi toàn cầu undefinedcó thể ghi được, vì vậy bất kỳ ai cũng có thể viết lại giá trị của nó. Đối số thứ ba (khi không được truyền một giá trị) tạo ra một biến có tên undefinednằm trong phạm vi không gian tên / hàm. Bởi vì không có giá trị nào được chuyển khi bạn tạo không gian tên, nó mặc định là giá trị undefined.


9
+1 cho mẫu tuyệt vời này. Đối với bất kỳ ai quan tâm, mẫu này là một phần trong bài thuyết trình tuyệt vời của Elijah Manor tại Mix 2011 (bỏ qua tiêu đề) live.visitmix.com/MIX11/Simes/Speaker/Elijah-Manor
Darren Lewis

11
Từ bài viết của Elijah, đây là những ưu và nhược điểm của phương pháp này, được diễn giải. Ưu điểm: 1. Các thuộc tính và phương thức công khai và riêng tư, 2. không sử dụng OLN cồng kềnh, 3. Bảo vệ không xác định 4. Đảm bảo rằng $ tham chiếu đến jQuery, 5. Không gian tên có thể mở rộng các tệp, Nhược điểm: Khó hiểu hơn OLN
Jared Beck

4
Điều này được gọi là IIFE ngày nay ( Biểu hiện chức năng được gọi ngay lập tức ). Cảm ơn câu trả lời của bạn +1!
Gustavo Gondim

20
@CpILL: không chắc có còn liên quan hay không, nhưng undefinedđối số thứ ba là nguồn của biến giá trị undefined. Trong khi làm việc với các trình duyệt cũ hơn / tiêu chuẩn javascript (ecmascript 5, javascript <1.8.5 ~ firefox 4), biến phạm vi toàn cầu undefinedcó thể ghi được, vì vậy bất kỳ ai cũng có thể viết lại giá trị của nó. Thêm thứ ba, đối số bổ sung mà bạn không chuyển sẽ làm cho nó có giá trị undefined, vì vậy bạn đang tạo phạm vi không gian tên undefinedsẽ không được ghi lại bởi các nguồn bên ngoài.
mrówa

4
@SapparentSun Lợi ích của việc window.skillet = window.skillet || {}này là cho phép nhiều tập lệnh thêm an toàn vào cùng một không gian tên khi chúng không biết trước theo thứ tự chúng sẽ thực hiện. Điều này có thể hữu ích nếu bạn muốn có thể sắp xếp lại các tập lệnh của bạn bao gồm tùy ý mà không phá vỡ mã của bạn hoặc nếu bạn muốn tải tập lệnh không đồng bộ với thuộc tính async và do đó không đảm bảo về thứ tự thực hiện. Xem stackoverflow.com/questions/6439579/
Mark Amery

338

Một cách khác để làm điều đó, mà tôi cho rằng nó ít hạn chế hơn một chút so với hình thức nghĩa đen của đối tượng, là:

var ns = new function() {

    var internalFunction = function() {

    };

    this.publicFunction = function() {

    };
};

Ở trên khá giống mô hình mô-đundù bạn có thích hay không , nó cho phép bạn trưng bày tất cả các chức năng của mình dưới dạng công khai, đồng thời tránh cấu trúc cứng nhắc của một đối tượng theo nghĩa đen.


16
1. Có sự khác biệt giữa OLN và mẫu mô-đun. 2. Tôi không / luôn luôn / thích OLN vì bạn phải nhớ không đặt dấu phẩy cuối cùng và tất cả các thuộc tính của bạn phải được khởi tạo với một giá trị (như null hoặc không xác định). Ngoài ra, nếu bạn cần đóng cửa cho các chức năng thành viên, bạn sẽ cần các nhà máy chức năng nhỏ cho mỗi phương thức đó. Một điều nữa là bạn phải bao gồm tất cả các cấu trúc điều khiển của mình bên trong các hàm, trong khi biểu mẫu trên không áp đặt điều đó. Điều đó không có nghĩa là tôi không sử dụng OLN, chỉ là đôi khi tôi không thích nó.
Ionuț G. Stan

8
Tôi thích cách tiếp cận này vì nó cho phép các hàm riêng, các biến và các hằng giả (ví dụ var API_KEY = 12345;).
Lawrence Barsanti

12
Tôi thích cái này tốt hơn thùng chứa đối tượng được phân tách bằng dấu phẩy được bình chọn cao hơn. Tôi cũng không thấy bất kỳ thiếu sót nào. Tui bỏ lỡ điều gì vậy?
Lucent

7
JS Newbie ở đây ... tại sao tôi không phải gõ ns().publicFunction(), đó là ... ns.publicFunction()hoạt động.
John Kraft

14
@ John John, đó là lý do của newtừ khóa trước functiontừ khóa. Về cơ bản, những gì đang làm là nó khai báo một hàm ẩn danh (và là một hàm, nó cũng là một hàm tạo), và sau đó nó ngay lập tức gọi nó như là một hàm tạo new. Như vậy, giá trị cuối cùng được lưu trữ bên trong nslà một thể hiện (duy nhất) của hàm tạo ẩn danh đó. Hy vọng nó có ý nghĩa.
Ionuț G. Stan

157

Có một cách thanh lịch hoặc cô đọng hơn để làm điều này?

Đúng. Ví dụ:

var your_namespace = your_namespace || {};

sau đó bạn có thể có

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

1
điều này cho tôi một lỗi trong IE7. var your_namespace = (typeof your_namespace == "không xác định" ||! your_namespace)? {}: your_namespace; làm việc tốt hơn
mjallday

9
nó phải là var your_namespace = your_namespace = your_namespace || {} Hoạt động trên mọi trình duyệt;)
Palo

+1 từ tôi! Thin one hoạt động như câu trả lời của Jaco Pretorius bằng cách mở rộng một thư viện thành các tệp khác nhau hoặc các vị trí khác nhau trong cùng một tệp. Chỉ cần rực rỡ!
centurian

2
@Palo Bạn có thể giải thích tại sao nó phải như thế này không? var your_namespace = your_namespace = your_namespace || {}
Sriram

6
bạn sẽ có khả năng mở rộng đối tượng your_namespace trong các tệp js khác nhau. Khi sử dụng var your_namespace = {}, bạn không thể làm điều đó, vì đối tượng sẽ bị ghi đè bởi mỗi tệp
Alex Pacurar

93

Tôi thường xây dựng nó trong một đóng cửa:

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

Phong cách của tôi trong những năm qua đã có một sự thay đổi tinh tế kể từ khi viết bài này, và bây giờ tôi thấy mình đang viết bài kết thúc như thế này:

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

Theo cách này, tôi thấy API công khai và việc triển khai dễ hiểu hơn. Hãy nghĩ về tuyên bố trở lại như là một giao diện công cộng để thực hiện.


3
Bạn có nên kiểm tra không MYNS.subns = MYNS.subns || {}??
Mirko

Một điểm tốt nên được thực hiện cho ý định của các nhà phát triển. Bạn cần xem xét những việc cần làm khi nó tồn tại, thay thế nó, lỗi, sử dụng kiểm tra phiên bản hiện có hoặc thay thế và có điều kiện thay thế. Tôi đã có những tình huống khác nhau đòi hỏi từng biến thể. Trong hầu hết các trường hợp, bạn có thể có trường hợp này là trường hợp có rủi ro thấp và việc thay thế có thể có lợi, hãy xem xét một mô-đun lừa đảo đã cố gắng chiếm quyền điều khiển NS.
Brett Ryan

1
Có một lời giải thích về cách tiếp cận này trong Sách "Nói Javascript" ở trang 412 nếu có ai có nó, dưới tiêu đề "Mô-đun nhanh và bẩn".
Soferio

2
Mẹo tối ưu hóa: trong khi var foo = functionfunction footương tự, là riêng tư; do tính chất được gõ động của JavaScript, cái sau nhanh hơn một chút vì nó bỏ qua một vài hướng dẫn trong hầu hết các đường ống của phiên dịch viên. Với var foo, hệ thống loại phải được gọi để tìm ra loại nào được gán cho var nói, trong khi đó function foo, hệ thống loại tự động biết đó là một chức năng, do đó, một vài cuộc gọi chức năng bị bỏ qua, dịch ra ít lệnh gọi CPU hơn như jmp, pushq, popq, vv, mà chuyển đến một đường ống CPU ngắn hơn.
Braden hay nhất

1
@brett ôi. Bạn đúng. Tôi đã nghĩ về một ngôn ngữ kịch bản khác. Mặc dù tôi vẫn nhấn mạnh function foocú pháp dễ đọc hơn. Và tôi vẫn thích phiên bản của tôi.
Braden hay nhất

56

Vì bạn có thể viết các tệp JavaScript khác nhau và sau đó kết hợp hoặc không kết hợp chúng trong một ứng dụng, mỗi tệp cần có khả năng khôi phục hoặc xây dựng đối tượng không gian tên mà không làm hỏng công việc của các tệp khác ...

Một tệp có thể có ý định sử dụng không gian tên namespace.namespace1:

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

namespace.namespace1.doSomeThing = function(){}

Một tệp khác có thể muốn sử dụng không gian tên namespace.namespace2:

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

namespace.namespace2.doSomeThing = function(){}

Hai tập tin này có thể sống cùng nhau hoặc xa nhau mà không va chạm.


1
Tôi đã thấy đây là một phương pháp rất hữu ích để tổ chức tập lệnh máy khách thành nhiều tệp trong các ứng dụng lớn, nơi chức năng cần phải được mô đun hóa.
DVK

Câu hỏi đặt câu hỏi cụ thể cho nhiều tệp: stackoverflow.com/questions/5150124/ từ
Ciro Santilli 病毒 审查 六四

49

Đây là cách Stoyan Stefanov thực hiện nó trong cuốn sách Mẫu JavaScript mà tôi thấy rất hay (nó cũng cho thấy cách anh ấy nhận xét cho phép tạo tài liệu API tự động và cách thêm phương thức vào nguyên mẫu của đối tượng tùy chỉnh):

/**
* 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

Tôi sử dụng phương pháp này:

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

Mã bên ngoài sau đó có thể là:

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

Chi tiết rõ ràng! Cảm ơn! Chỉ cần tự hỏi những gì bạn có trên Namespace.js. Tôi chưa bao giờ sử dụng nó, vì vậy tôi tự hỏi nếu một người có kiến ​​thức / kỹ năng / kinh nghiệm của bạn sẽ xem xét sử dụng nó.
Giăng

Tôi thích nó! Mặt khác, tôi nhận được ngoại lệ trên dòng đầu tiên của mã bên ngoài này, nói: 'myNameSpace.MyClass' [không xác định] không phải là hàm tạo. có lẽ nó phụ thuộc vào việc triển khai JS? : /
yoosiba

@yossiba: Có thể. Các mã ở trên là công cụ khá chuẩn. Trong tiêu chuẩn JS, bất kỳ hàm nào cũng có thể được sử dụng như một hàm tạo, bạn không cần phải làm gì để đánh dấu một hàm cụ thể để được sử dụng như một hàm tạo. Bạn đang sử dụng một hương vị khác thường như ActionScript hay cái gì đó?
AnthonyWJones

@Anthony tốt hơn là sử dụng var MYNAMESPACE = MYNAMESPACE || {}; chỉ sử dụng var myNamespace = {} là không an toàn và hơn nữa tốt hơn là khai báo không gian tên của bạn trong mũ
paul

9
@paul: "Tốt hơn" có thể khá chủ quan. Tôi ghét đọc mã mà NÊN tại tôi vì vậy tôi tránh sử dụng mã định danh sử dụng tất cả chữ hoa. Trong khi ns = ns || {}có vẻ phòng thủ hơn, nó có thể dẫn đến kết quả bất ngờ khác.
AnthonyWJones

32

Đây là liên kết tiếp theo của user106826 với Namespace.js. Có vẻ như dự án đã chuyển sang GitHub . Bây giờ là smith / nampacesotjs .

Tôi đã sử dụng trình trợ giúp JavaScript đơn giản này cho dự án nhỏ của mình và cho đến nay nó có vẻ nhẹ nhưng đủ linh hoạt để xử lý không gian tên tải các mô-đun / lớp. Sẽ thật tuyệt nếu nó cho phép tôi nhập một gói vào một không gian tên mà tôi chọn, không chỉ là không gian tên toàn cầu ... thở dài, mà còn bên cạnh vấn đề.

Nó cho phép bạn khai báo không gian tên sau đó xác định các đối tượng / mô-đun trong không gian tên đó:

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

Một tùy chọn khác là khai báo không gian tên và nội dung của nó cùng một lúc:

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

Để biết thêm các ví dụ sử dụng, hãy xem tệp example.js trong nguồn .


2
Miễn là bạn nhớ điều này có một số hàm ý về hiệu suất, vì mỗi lần bạn truy cập my.awclaw.package.WildClass bạn đang truy cập vào tài sản tuyệt vời của tôi, thuộc tính gói của my.awemme và thuộc tính WildClass của my.awemme. gói.
SamStephens

29

Mẫu vật:

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

Bạn có thể tùy ý khai báo một localbiến, samethích selfvà gán local.onTimeoutnếu bạn muốn nó ở chế độ riêng tư.


14

Bạn có thể khai báo một hàm đơn giản để cung cấp không gian tên.

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

Nếu bạn cần phạm vi riêng tư:

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

khác nếu bạn sẽ không bao giờ sử dụng phạm vi riêng tư:

var yourNamespace = {};

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

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

yourNamespace.publicMethod();

12

Mẫu Mô-đun ban đầu được định nghĩa là một cách để cung cấp cả đóng gói riêng và công khai cho các lớp trong công nghệ phần mềm thông thường.

Khi làm việc với mẫu Mô-đun, chúng tôi có thể thấy hữu ích khi xác định một mẫu đơn giản mà chúng tôi sử dụng để bắt đầu với mẫu này. Đây là một trong đó bao gồm các biến khoảng cách tên, công khai và riêng tư.

Trong JavaScript, mẫu Mô-đun được sử dụng để mô phỏng thêm khái niệm các lớp theo cách chúng ta có thể bao gồm cả các phương thức công khai / riêng và các biến trong một đối tượng, do đó che chắn các phần cụ thể khỏi phạm vi toàn cầu. Điều này dẫn đến kết quả là giảm khả năng tên hàm của chúng ta xung đột với các hàm khác được xác định trong các tập lệnh bổ sung trên trang.

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

    }
  };

})();

Ưu điểm

Tại sao mô-đun Mô-đun là một lựa chọn tốt? Đối với người mới bắt đầu, các nhà phát triển đến từ nền tảng hướng đối tượng sẽ sạch sẽ hơn rất nhiều so với ý tưởng đóng gói thực sự, ít nhất là từ phối cảnh JavaScript.

Thứ hai, nó hỗ trợ dữ liệu riêng tư - vì vậy, trong mẫu Mô-đun, các phần công khai của mã của chúng tôi có thể chạm vào các phần riêng tư, tuy nhiên thế giới bên ngoài không thể chạm vào các phần riêng tư của lớp.

Nhược điểm

Nhược điểm của mẫu Mô-đun là khi chúng tôi truy cập cả thành viên công cộng và tư nhân khác nhau, khi chúng tôi muốn thay đổi mức độ hiển thị, chúng tôi thực sự phải thay đổi từng địa điểm mà thành viên đã sử dụng.

Chúng tôi cũng không thể truy cập các thành viên tư nhân trong các phương thức được thêm vào đối tượng sau đó . Điều đó nói rằng, trong nhiều trường hợp, mô-đun Mô-đun vẫn khá hữu ích và khi được sử dụng đúng cách, chắc chắn có khả năng cải thiện cấu trúc ứng dụng của chúng tôi.

Mô hình mô-đun tiết lộ

Bây giờ chúng ta đã quen thuộc hơn với mô hình mô-đun, chúng ta hãy xem xét một phiên bản cải tiến hơn một chút - mô hình Mô-đun tiết lộ của Christian Heilmann.

Mẫu Mô-đun tiết lộ xuất hiện khi Heilmann thất vọng với việc anh ta phải lặp lại tên của đối tượng chính khi chúng tôi muốn gọi một phương thức công khai từ một phương thức khác hoặc truy cập các biến công khai. Anh ta cũng không thích yêu cầu của mô hình Mô-đun vì phải chuyển đổi để phản đối ký hiệu theo nghĩa đen cho những điều anh ta muốn công khai.

Kết quả của những nỗ lực của anh ấy là một mô hình cập nhật, trong đó chúng tôi chỉ cần xác định tất cả các hàm và biến của chúng tôi trong phạm vi riêng tư và trả về một đối tượng ẩn danh có con trỏ tới chức năng riêng tư mà chúng tôi muốn tiết lộ là công khai.

Một ví dụ về cách sử dụng mẫu Mô-đun tiết lộ có thể được tìm thấy bên dưới

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

Ưu điểm

Mẫu này cho phép cú pháp của các tập lệnh của chúng tôi phù hợp hơn. Nó cũng làm cho nó rõ ràng hơn ở phần cuối của mô-đun mà các chức năng và biến của chúng ta có thể được truy cập công khai, giúp giảm bớt khả năng đọc.

Nhược điểm

Một nhược điểm của mẫu này là nếu một chức năng riêng đề cập đến một chức năng công cộng, thì chức năng công cộng đó không thể bị ghi đè nếu cần một bản vá. Điều này là do chức năng riêng tư sẽ tiếp tục đề cập đến việc triển khai riêng tư và mẫu không áp dụng cho các thành viên công cộng, chỉ áp dụng cho các chức năng.

Các thành viên đối tượng công cộng tham chiếu đến các biến riêng tư cũng phải tuân theo các ghi chú quy tắc không vá ở trên.


9

Tôi đã tạo ra không gian tên được lấy cảm hứng từ các mô-đun của Erlang. Đó là một cách tiếp cận rất chức năng, nhưng đó là cách tôi viết mã JavaScript của mình những ngày này.

Nó cung cấp cho một bao đóng một không gian tên toàn cục và hiển thị các hàm tập xác định trong bao đóng đó.

(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

Sau khi chuyển một số thư viện của tôi sang các dự án khác nhau và phải liên tục thay đổi không gian tên cấp cao nhất (được đặt tên tĩnh), tôi đã chuyển sang sử dụng hàm trợ giúp nhỏ (nguồn mở) này để xác định không gian tên.

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

Mô tả về các lợi ích là tại bài viết trên blog của tôi . Bạn có thể lấy mã nguồn ở đây .

Một trong những lợi ích tôi thực sự thích là cách ly giữa các mô-đun đối với thứ tự tải. Bạn có thể tham khảo một mô-đun bên ngoài TRƯỚC KHI nó được tải. Và tham chiếu đối tượng bạn nhận được sẽ được điền khi mã có sẵn.


1
Tôi đã tạo một phiên bản cải tiến (2.0) của thư viện không gian tên: code.google.com/p/pageforest/source/browse/appengine/static/src/iêu
mckoss

tất cả các liên kết của bạn dường như đã chết
snoobdog 23/07/19

8

Tôi sử dụng cú pháp sau đây cho không gian tên.

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

Tôi đến dự tiệc muộn 7 năm, nhưng đã làm khá nhiều việc trong khoảng 8 năm trước:

Điều quan trọng là có thể dễ dàng và hiệu quả tạo nhiều không gian tên lồng nhau để giữ cho một ứng dụng web phức tạp được tổ chức và quản lý, đồng thời tôn trọng không gian tên toàn cầu JavaScript (ngăn ngừa ô nhiễm không gian tên) và không làm tắc nghẽn bất kỳ đối tượng hiện có nào trong đường dẫn không gian tên trong khi thực hiện như vậy .

Từ những điều trên, đây là giải pháp vào khoảng năm 2008 của tôi:

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

Điều này không tạo ra một không gian tên, nhưng cung cấp một chức năng để tạo các không gian tên.

Điều này có thể được cô đọng thành một lớp lót rút gọn:

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

Ví dụ sử dụng:

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

Hoặc, như một tuyên bố:

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

Hoặc sau đó được thực thi như:

com.example.namespace.test();

Nếu bạn không cần hỗ trợ cho các trình duyệt cũ, phiên bản cập nhật:

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

Bây giờ, tôi sẽ không hài lòng với việc tiếp xúc namespacevới không gian tên toàn cầu. (Quá tệ, ngôn ngữ cơ sở không cung cấp điều này cho chúng tôi!) Vì vậy, tôi thường sử dụng ngôn ngữ này trong một bao đóng, chẳng hạn như:

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

Trong một ứng dụng lớn hơn, điều này chỉ cần được xác định một lần khi bắt đầu tải trang (đối với các ứng dụng web dựa trên máy khách). Các tệp bổ sung sau đó có thể sử dụng lại chức năng không gian tên nếu được giữ (bao gồm dưới dạng "tùy chọn" ở trên). Tệ nhất, nếu chức năng này được khai báo lại một vài lần - đó chỉ là một vài dòng mã và ít hơn nếu được rút gọn.


3

Tôi nghĩ rằng tất cả các bạn sử dụng quá nhiều mã cho một vấn đề đơn giản như vậy. Không cần phải làm một repo cho điều đó. Đây là một chức năng dòng duy nhất.

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

Thử nó :

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


2

Tôi thích giải pháp của Jaco Pretorius, nhưng tôi muốn làm cho từ khóa "này" hữu ích hơn một chút bằng cách trỏ nó vào đối tượng mô đun / không gian tên. Phiên bản chảo của tôi:

(function ($, undefined) {

    console.log(this);

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

2

Mẫu yêu thích của tôi đã trở thành gần đây này:

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() {
    // ...
  }
  
})();

Tất nhiên, trả về có thể ở cuối, nhưng nếu chỉ khai báo hàm theo nó, sẽ dễ dàng hơn nhiều để xem không gian tên là gì và API được hiển thị.

Mô hình sử dụng các biểu thức hàm trong các trường hợp như vậy dẫn đến việc không thể biết phương thức nào được đưa ra mà không đi qua toàn bộ mã.


Xin chào, làm thế nào để bạn gọi các chức năng công cộng từ đoạn trích của bạn? Tôi đã thửnamespace.a();
olimart

@olivier vâng, đó là ý tưởng. Mặc dù bây giờ với ES6, tôi thường sử dụng cú pháp tốc ký của các đối tượng bằng chữ ( ponyfoo.com/articles/es6-object-literal-features-in-depth )
Nomaed

1

Nếu sử dụng Makefile bạn có thể làm điều này.

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

Tôi thích sử dụng Makefile hơn bất cứ khi nào tôi nhận được khoảng 1000 dòng vì tôi có thể nhận xét một cách hiệu quả những dòng mã lớn bằng cách xóa một dòng duy nhất trong tệp tạo tệp. Nó làm cho nó dễ dàng để mân mê với công cụ. Ngoài ra, với kỹ thuật này, không gian tên chỉ xuất hiện một lần trong khúc dạo đầu để dễ dàng thay đổi và bạn không phải lặp lại nó trong mã thư viện.

Một tập lệnh shell để phát triển trực tiếp trong trình duyệt khi sử dụng tệp tạo tệp:

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

Thêm điều này như một nhiệm vụ 'đi' và bạn có thể 'thực hiện' để giữ cho bản dựng của bạn được cập nhật khi bạn viết mã.


1

Khá theo dõi câu trả lời của Ionuț G. Stan, nhưng cho thấy lợi ích của mã var ClassFirst = this.ClassFirst = function() {...}không bị xáo trộn bằng cách sử dụng , lợi dụng phạm vi đóng của JavaScript để làm lộn xộn không gian tên cho các lớp trong cùng một không gian tên.

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

Đầu ra:

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

Tôi đã viết một thư viện không gian tên khác hoạt động giống như các gói / đơn vị làm bằng các ngôn ngữ khác. Nó cho phép bạn tạo một gói mã JavaScript và tham chiếu gói đó từ mã khác:

Tập tin hello.js

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

Tệp example.js

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

Chỉ có tập tin thứ hai cần được đưa vào trang. Phụ thuộc của nó (tệp hello.js trong ví dụ này) sẽ tự động được tải và các đối tượng được xuất từ ​​các phụ thuộc đó sẽ được sử dụng để điền vào các đối số của hàm gọi lại.

Bạn có thể tìm thấy dự án liên quan trong Gói JS .


1
@ peter-mortensen Những chỉnh sửa này cho câu trả lời của tôi từ '11 có thực sự cần thiết không? Đó chắc chắn không phải là phá hoại những gì bạn đang làm, đừng hiểu sai ý tôi, nhưng chúng rất hời hợt. Tôi muốn vẫn là tác giả duy nhất của bài viết như thế này trừ khi bạn thực sự thêm một cái gì đó tốt.
Stijn de Witt

1

Chúng ta có thể sử dụng nó một cách độc lập theo cách này:

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

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

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

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

0

Thói quen của tôi là sử dụng hàm myName () làm nơi lưu trữ thuộc tính và sau đó var myName làm chủ sở hữu "phương thức" ...

Cho dù điều này là đủ hợp pháp hay không, đánh bại tôi! Tôi đang dựa vào logic PHP của mình mọi lúc và mọi thứ chỉ đơn giản là hoạt động. : 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();

Bạn cũng có thể thực hiện theo cách 'ngược lại' để kiểm tra trước khi tạo đối tượng tốt hơn nhiều :

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

Tham khảo điều này: JavaScript: Tạo đối tượng với Object.create ()


0

Trong JavaScript không có phương thức được xác định trước để sử dụng không gian tên. Trong JavaScript, chúng ta phải tạo các phương thức riêng để xác định NameSpaces. Đây là một quy trình chúng tôi tuân theo trong các công nghệ Oodles.

Đăng ký một NameSpace Sau đây là chức năng đăng ký một không gian tên

//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]];
 }
}

Để đăng ký một Không gian tên, chỉ cần gọi hàm trên với đối số là không gian tên được phân tách bằng '.'(dấu chấm). Ví dụ Hãy để tên ứng dụng của bạn là oodles. Bạn có thể tạo một không gian tên bằng phương pháp sau

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

Về cơ bản, nó sẽ tạo cấu trúc NameSpaces của bạn như bên dưới trong phần phụ trợ:

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

Trong hàm trên, bạn đã đăng ký một không gian tên được gọi "oodles.HomeUtilities""oodles.GlobalUtilities". Để gọi các không gian tên này, chúng ta tạo một biến tức là var $OHUvà var $OGU.

Các biến này không là gì ngoài một bí danh để Intializing không gian tên. Bây giờ, bất cứ khi nào bạn khai báo một hàm thuộc về HomeUtilitiesbạn sẽ khai báo nó như sau:

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

Trên đây là khởi tạo tên hàm và nó được đặt vào một không gian tên $OHU. và để gọi chức năng này bất cứ nơi nào trong các tập tin kịch bản. Chỉ cần sử dụng mã sau đây.

$OHU.initialization();

Tương tự, với các NameSpaces khác.

Hy vọng nó giúp.


0

JavaScript không hỗ trợ không gian tên theo mặc định. Vì vậy, nếu bạn tạo bất kỳ phần tử nào (hàm, phương thức, đối tượng, biến) thì nó sẽ trở thành toàn cục và gây ô nhiễm không gian tên toàn cục. Hãy lấy một ví dụ về việc xác định hai hàm mà không có bất kỳ không gian tên nào,

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

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

Nó luôn gọi định nghĩa hàm thứ hai. Trong trường hợp này, không gian tên sẽ giải quyết vấn đề va chạm tên.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.