1

在 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

希望这是有道理的。

4

2 回答 2

2

所以你想要每个实例受保护的属性?也就是说,由ObjectA,ObjectB等创建的实例的属性,但只能由库内的代码访问,而不能由库外的代码访问?

您目前无法在 JavaScript 中正确执行此操作,但您将能够在下一个版本中使用私有名称对象。(尽管你现在可以在 ES5 中做类似的事情,但请参阅下面的“几乎做到了”。)

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

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

    // ...
})();

在那里定义的所有函数(你的ObjectA等)都可以访问那个sharedData变量,这是从外部完全无法访问的。但这不是每个实例,由ObjectA,ObjectB等创建的每个对象都没有自己的副本。

几乎做到了

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

实例| 来源

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不是字符串,而是私有名称对象。

于 2012-12-05T13:12:39.937 回答
1

好吧,您将“只”需要在Context声明这些变量Lib

window.Lib = (function()
{
    var _c = 42;   

    return {
    };
});

请注意,我删除了该伪构造函数的自动调用。这意味着,您需要为多个实例创建多个调用,每个实例都有自己独特的一组值。Lib()

var inst1 = Lib(),
    inst2 = Lib();

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

于 2012-12-05T13:11:37.253 回答