1

抱歉,我可能在这里是个菜鸟……但是:

我有以下 javascript 对象:

jeeni.TextField = (function(){

    var tagId;

    privateMethod = function(){
        console.log("IN: privateMethod");
    }

    publicMethod = function(){
        console.log("IN: publicMethod: " + this.tagId);
    }

    jeeni.TextField = function(id){
        console.log("Constructor");
        this.tagId = id;
    }

    jeeni.TextField.prototype = {
            constructor: jeeni.TextField,
            foo: publicMethod
    };

    return jeeni.TextField;
 }());

现在,当我运行以下代码时,我得到了相应的结果:

var textField1 = new jeeni.TextField(21); // Outputs: Constructor
textField1.foo();           // Outputs: IN: publicMethod: 21
console.log(textField1.tagId); // Outputs: 21
console.log(textField1.privateMethod); // Outputs: undefined

所以我的问题是为什么是privateMethod隐藏的而tagId不是隐藏的。我希望它们都是私有范围。

请帮助一个菜鸟。

谢谢

4

4 回答 4

2

您混合了两种创建模块的方法。问题是私人var tagId不是一回事this.tagId

让我从一个有效的版本开始,它与我通常使用 AMD 模块所做的类似:

jeeni.TextField = (function(){
//here you can put a variable common to all instances
  return {
  init:function(id){
    var tagId = id;
    console.log("Constructor");

    function privateMethod(){
        console.log("IN: privateMethod");
    }

    function publicMethod(){
        console.log("IN: publicMethod: " + tagId);
    }

    return {
       foo:publicMethod
    };
  }
})();

var textField1 = jeeni.TextField.init(21); //creates instance
textField1.foo();           // Outputs: IN: publicMethod: 21
console.log(textField1.tagId); // Outputs: undefined
console.log(textField1.privateMethod); // Outputs: undefined

这有一个缺点:对于每个对象实例,函数都被复制到内存中。这是使用原型的唯一原因。但是如果你想要私有变量,你可能会浪费一些内存。

在您的代码中,如果您替换this.tagId为 justtagId您将使用私有变量,但它只是一个,对于所有实例都是通用的。

如果我找到一种方法,我会考虑让你的代码工作和编辑。

[编辑]

Stephen 所做的与您期望的代码所做的非常接近。不过,我不想向同事解释它是如何工作的以及为什么。

[编辑]

顺便提一句。看看 require.js 和 AMD(模块定义)

于 2012-11-25T22:29:43.803 回答
1

您的代码几乎是正确的。this访问tagId时就输了。this.tagId正在该特定 TextField 上设置属性,而不是var tagId您在顶部声明的属性。

如果您x不使用设置变量var,如在 中x = 2,它会找到最近x的作用域链;如果没有找到,它最终会成为全局对象 ( window) 的一个属性。在您的情况下,您可以tagId从任何这些功能进行修改,因为它们可以达到上一层。这就是为什么它被用作“私有”变量的原因,privateMethod可以达到同样的方式。

而且由于jeeni.TextField被设置为整个函数的返回值,所以你不要jeeni在里面引用。

jeeni.TextField = (function(){

    var tagId;

    var privateMethod = function(){
        console.log("IN: privateMethod");
    }

    var publicMethod = function(){
        console.log("IN: publicMethod: " + tagId);
    }

    function TextField(id){
        // this === the new instance of TextField
        console.log("Constructor", this);
        // set the tagId variable which is in scope here
        tagId = id;
    }

    TextField.prototype.foo = publicMethod

    return TextField

 }()); 

请注意,我还在var公共/私有方法之前添加了,否则您会将它们泄漏到全局范围内。您也可以使用function publicMethod(){ ... },这通常更适合调试。

如果您感到困惑,x = function(){}...则与function x(){}. 请参阅“揭秘命名函数表达式”的第 2 章。

清除后,这可能仍然不是您想要的,因为此代码tagId将在 TextField 的所有实例之间共享。每个实例都有一个的通常方法是tagId将其设为“公共”属性,这正是您所做的(我认为这没有任何问题,因为id它本身来自外部):

...

// var tagId -> drop this

publicMethod = function(){
    console.log("IN: publicMethod: " + this.tagId);
}

function TextField(id){
    console.log("Constructor");
    this.tagId = id;
}

现在让我们tagId为每个实例设置私有且特定的。无需每次都创建新函数的最简单方法是创建一个私有对象(我们称之为它tags)来保存所有数据,并为每个实例提供它自己的 ID(tf_id)。ID 是可见的,但由于该tags对象是私有的,因此您只能使用以下一种公共方法访问数据:

jeeni.TextField = (function(){

    var tags = {}
      , uid = 0;

    privateMethod = function(){
        console.log("IN: privateMethod");
    }

    publicMethod = function(){
        console.log("IN: publicMethod: " + tags[this.tf_id].tagId);
    }

    function TextField(id){
        this.tf_id = uid++
        tags[this.tf_id] = id
    }

    TextField.prototype.foo = publicMethod

    return TextField

 }());
于 2012-11-25T22:53:11.147 回答
0

显然我读错了这个问题。

如果您设置某物的属性(即 this.tagId),它会立即可供全世界访问。您将构造函数包装在错误的位置 - 改为将其更改为:

jeeni.TextField = (function(){
    var TextField = function(id){
        console.log("Constructor");
        var tagId = id;
        privateMethod = function(){
            console.log("IN: privateMethod");
        }

        this.publicMethod = function(){
            console.log("IN: publicMethod: " + tagId);
        }
    }

    TextField.prototype = {
            constructor: TextField
    };

    return TextField;
}());

基本上,您将无法在原型上放置任何需要访问这些受保护变量的东西。

于 2012-11-25T22:07:52.910 回答
0

这里有几个问题。首先是您以不正常的方式处理全局变量。实际上你有这个:

var globalVar;

globalVar = function() {
    globalVar = function() {
        //... stuff
    }
    return globalVar;
}

你希望它能做什么?正常的写法是:

function myObj(id) {
    // in javascript the function *is* the constructor
    tagId = id;
    function someMethod() {
        //...
    }
}
var globalVar = new myObj(someId);

尽管最好避免使用全局变量。

在您给出的示例中,这是您要返回的实际对象:

 function(id){
    console.log("Constructor");
    this.tagId = id;
}

所以不,它没有privateMethodor publicMethod。由于此函数对象是在封闭函数的范围内构造的,因此构造函数可以访问privateMethod,这就是它的隐私来自何处。

要了解如何在 javascript 中执行私有变量和方法,请自行前往向导:

http://javascript.crockford.com/private.html

于 2012-11-25T22:36:52.477 回答