有一种在JavaScript中创建“内部”属性的整洁方法吗?

时间:2012-12-05 13:08:04

标签: javascript structure

在JavaScript中,我可以使用这样的顶级对象开始编写“库”或功能集合:

window.Lib = (function()
{
    return {

        // Define Lib here.
        //

    };

})();

我还可以在Lib中添加一些用于创建与之相关的对象的函数:

window.Lib = (function()
{
    return {

        ObjectA: function()
        {
            var _a = 5;

            return {

                getA: function(){ return _a; }

            };

        },


        ObjectB: function()
        {
            var _b = 2;
            var _c = 1;

            return {

                getB: function(){ return _b; }

            };

        }

    };

})();

将使用如下:

var thing = Lib.ObjectA();
var thing2 = Lib.ObjectA();
var thing3 = Lib.ObjectB();

我可以使用上面创建的每个方法中的方法来获取在_a中定义的ObjectA()_b内定义的ObjectB()的值:

alert(thing.getA()); // 5
alert(thing3.getB()); // 2

我想要实现的目标是:

我想要访问属性_c(在ObjectB()中定义),但仅在Lib 范围内。我怎么能这样做?我的意思是,我希望在我在Lib()返回的对象中定义的任何函数中使属性可读,但我不想在那之外公开这些值。

代码示例:

window.Lib = (function()
{
    return {

        ObjectA: function(){ ... },
        ObjectB: function(){ ... },

        assess: function(obj)
        {
            // Somehow get _c here.
            alert( obj.getInternalC() );
        }

    };

})();

哪个会这样:

var thing = Lib.ObjectB();
alert( thing.getInternalC() ) // error | null | no method named .getInternalC()

Lib.assess(thing); // 1

希望这是有道理的。

2 个答案:

答案 0 :(得分:2)

所以你想要每个实例受保护的属性?也就是说,ObjectAObjectB等创建的实例的属性,但是 可以访问库中的代码,而不是代码外的代码?

您目前无法在JavaScript中正确执行 ,但您可以使用private name objects在下一版本中执行 。 (请参阅下面的“几乎要做”,以了解ES5中现在可以做的类似事情。)

很容易创建由Lib中的所有代码共享的数据,而不是每个实例属性,如下所示:

window.Lib = (function()
{
    var sharedData;

    // ...
})();

在那里定义的所有功能(您的ObjectA等)都可以访问那个从外部无法访问的sharedData变量。但不是每个实例,ObjectAObjectB等创建的每个对象都没有自己的副本。

几乎做到了

如果您的代码将在具有ES5的环境中运行(因此,任何现代浏览器,“现代”包括IE8或更早版本),您可以隐藏但实际上不是私人财产,通过Object.defineProperty。这类似于私有名称对象在ES.next中的工作方式,但不是真正的私有:

Live Example | Source

window.Lib = (function() {
    // Get a random name for our "c" property
    var c = "__c" + Math.round(Math.random() * 1000000);

    // Return our library functions
    return {

        ObjectA: function() {
            // Create an object with a couple of public proprties:
            var obj = {
                pub1: "I'm a public property",
                pub2: "So am I"
            };

            // Add our obscured "c" property to it, make sure it's
            // non-enumerable (doesn't show up in for-in loops)
            Object.defineProperty(obj, c, {
                enumerable: false,  // false is actually the default value, just emphasizing
                writable:   true,
                value:      "I'm an obscured property"
            });

            // Return it
            return obj;
        },

        ObjectB: function(){ /* ... */ },

        assess: function(obj) {
            // Here, we access the property using the `c` variable, which
            // contains the property name. In JavaScript, you can access
            // properties either using dotted notation and a literal
            // (`foo.propName`), or using bracketed notation and a string
            // (`foo["propName"]`). Here we're using bracketed notation,
            // and our `c` string, which has the actual property name.
            display( obj[c] );
        },


        alter: function(obj, value) {
            // Similarly, we can change the value with code that has
            // access to the `c` variable
            obj[c] = value;
        }
    };
})();

并像这样使用它:

// Create our object
var o = Lib.ObjectA();

// Play with it
display("pub1: " + o.pub1); // displays "pub1: I'm a public property"
display("c: " + o.c);       // displays "c: undefined" since `o` has no property called `c`
Lib.assess(o);              // displays "I'm an obscured property"

// Note that our obscured property doesn't show up in for-in loops or Object.keys:
var propName, propNames = [];
for (propName in o) {
    propNames.push(propName);
}
display("propNames: " + propNames.join(","));
display("Object.keys: " + Object.keys(o).join(","));

// Our Lib code can modify the property
Lib.alter(o, "Updated obscured property");
Lib.assess(o);

Lib.ObjectA返回的对象有一个属性,每次加载Lib时,其名称都会更改,而且不可枚举(不会显示在for-in循环中)。获得它的唯一方法是知道它的名称(再次,每次创建Lib时更改 - 例如,每个页面加载)。 Lib中的代码知道属性名称是什么,因为它位于所有c代码共享的Lib变量中。由于您可以使用括号表示法和字符串访问属性,因此我们可以使用instance[c]来访问该属性。

你看到这些是如何模糊不清的。 Lib之外的代码在枚举对象中的属性时看不到隐藏的属性,并且它们不知道我们为其分配的半随机名称,因此找不到该属性。当然,您可以通过使用调试器进行检查来找到它,但调试器可以做很多事情。

事实上,这是私有属性在ES.next中的工作方式,除了c不是字符串,它将是一个私有名称对象。

答案 1 :(得分:1)

好吧,你只需“声明 <{>} 中的那些变量

Lib

请注意,我删除了伪构造函数自动调用。这意味着,您需要为多个实例创建多个window.Lib = (function() { var _c = 42; return { }; }); 调用,每个实例都有自己的唯一值。

Lib()

如果您只想从所有子上下文(函数)中获得共享访问,您可以使用您已经执行的相同模式(仅移动 var 声明到父上下文,如上所示。)