【问题标题】:Why these closure based private variables in JavaScript not working为什么 JavaScript 中这些基于闭包的私有变量不起作用
【发布时间】:2013-05-06 14:07:16
【问题描述】:

我正在尝试在 JS 中模仿类似 OOP 的行为。我试图在函数Person 中有(私有)变量:idname。对于这个函数,我传递了用于初始化(私有)变量的参数。然后我返回的对象具有 name 的 getter 和 setter,并且只有 id 的 getter,从而有效地将 id 设为只读。

所以 id 只能通过构造函数设置,而 name 可以随时设置和获取。

这是代码:

     var Person = function (_id,_nm) {
        var id, name;    
                  
        this.id = _id;
        this.name = _nm;

        return {               
            setName: function (nm) {
                name = nm;
            },
            getName: function () {
                return name;
            },
            getId: function () {
                return id;
            },
            print: function () {
                document.writeln("Id: "+id+"<br />Name: "+name);
            }
        }
    }

    var person = new Person(123, "Mahesh");
    person.print();

但是,当new Person(123,"Mahesh") 执行时,我不明白它实际上是否设置了idname,因为在调试时我可以看到将鼠标悬停在它们上时设置了适当的值,但本地面板没有显示它们已初始化:

或者在 print() 中没有引用所需的 idname 变量:

这里有什么问题?

【问题讨论】:

  • 你为什么使用 IE? O.o
  • 嘿嘿好的菜鸟在这里:P 你用什么? IE有这么差吗?它只是舒服。看看它看起来很干净。
  • 是的,它很糟糕(如果低于 IE10)..我不知道为什么它很糟糕,因为这里不是为了评论,但我建议你使用 Firefox 或 Chrome。
  • 你应该看看Javascript: Do I need to put this.var for every variable in an object?,了解属性(“public”)和变量(“private”)之间的区别

标签: javascript oop closures


【解决方案1】:

Working fiddle

原因是因为您将public 成员用于Person.prototype。您不需要为这两个添加 this 引用。所以删除:

this.id = _id;
this.name = _nm;

并简单地使用:

 var id = _id,
     name = _nm;  

现在一切都会好起来的。整个想法是使用var,而不是this,否则不会创建闭包。现在您将无法直接访问nameid,而必须使用setName(), getName(), setId(), getId() 等。

idname 这两个成员现在将按照您的意愿变成闭包。

更新

如果你使用this.id,那么它就不会是private,你可以直接使用var p = new Person(1, "Mahesha"); 并访问p.namep.id。他们应该是private 所以这不是你想要的。

使用闭包模式,p.name 和 p.id 是未定义的,只能通过p.getName();p.getId(); 访问。阅读闭包是如何工作的。这个想法是,因为您使用的是 var name,所以将创建一个闭包来记住它的值。

您的getNamesetName 正在使用该闭包来访问name 属性。没有this.name,有一个通过高阶闭包记住的值。

【讨论】:

  • 完美针尖但还不能完全理解:1.this.idthis.name 如果没有关闭,到底去哪儿了? 2.无法理解您所说的“原因是因为您将公共成员用于 Person.prototype。”
  • 当我使用this.id=_idthis.name=_nm 时,我无法以p.namep.id 的身份访问它们
  • 是的,这对我也有用,我的意思是当 this.id = _idthis.name = _nm 像您在之前的评论中所说的那样保留时,document.writeln(p.name)document.writeln(p.id) 不起作用
【解决方案2】:

this.idvar id 不一样。 this.id 是对象的属性。 var id 属于本地范围。

【讨论】:

    【解决方案3】:

    要么使用new,要么返回一个值。不是两者。

    问题是您正在使用 new 关键字创建 Person 的新实例,但您的构造函数正在返回另一个对象。

    当您从构造函数返回某些内容时,它会返回该值,而不是函数的实例。

    当您执行new Person(123, "Mahesh") 时,您会看到创建了Person 的新实例。这可以在构造函数中作为this 访问。

    如果你的构造函数没有返回任何东西,那么 JavaScript 会自动返回 this。但是,您正在显式返回另一个对象。

    难怪var person 没有idname 属性(您只在this 上定义)。

    此外,print 不显示 idname,因为尽管您声明了它们 (var id, name),但您没有给它们任何值。因此他们是undefined

    这就是我将如何重写你的 Person 构造函数:

    function Person(id, name) {
        this.getId = function () {
            return id;
        };
    
        this.getName = function () {
            return name;
        };
    
        this.setName = function (new_name) {
            name = new_name;
        };
    
        this.print = function () {
            document.writeln("Id: " + id + "<br/>Name: " + name);
        };
    }
    

    我没有在this 上设置idname 属性,因为包含它们没有意义。

    【讨论】:

    • 这里使用new其实没问题。
    • @Bergi - 我同意。无论如何,OP 都明确地返回了一个对象。但是我相信他想创建一个Person 的实例。
    【解决方案4】:

    您混淆了对_id_nm 和“公共”实例属性(this.idthis.nm)使用局部范围(“私有”)变量。

    在这种情况下,您需要前者,但您创建了两者并且只初始化了后者。

    请注意,由于id 是只读的,因此您根本不需要单独的局部变量,您可以将词法范围的第一个参数用于构造函数:

    var Person = function (id, _nm) {
        var name = _nm;
    
        ...
    };
    

    【讨论】:

    • 请称它们为“属性”,而不是“变量”,但你是对的
    【解决方案5】:

    让我尝试使用以下内容进行解释:

    // Scope 1
    var Person = function (_id,_nm) {
        // Scope 2
        var id, name;    
    
        this.id = _id;
        this.name = _nm;
    
        return {
            // Scope 3
            setName: function (nm) {
                name = nm;
            },
            getName: function () {
                return name;
            },
            getId: function () {
                return id;
            },
            print: function () {
                $("#output").append("<p>Id: "+id+"; Name: "+name  + "<p/>");
            }
        }
    }
    

    print 方法将返回 undefined,因为您指的是从未在您的代码中设置的 var id, name;。您将_id_name 设置为属性idname,但您无法返回刚刚创建的对象。相反,您返回一个字典,它引用了您在 Scope 2 中创建的变量 nameid 您从未设置过,因此在 print() 调用中未定义输出。

    这是你应该做的:

    var NewPerson = function (_id,_nm) {
        var self = this;
        this.id = _id;
        this.name = _nm;  
    
        this.print = function () {
            $("#output").append("<p>New Person Id: "+this.id+"; Name: "+ this.name  + "<p/>");
        };
    
        return this;
    };
    
    var nperson = new NewPerson(456, "Marcos");
    nperson.print();
    

    这将输出:

    New Person Id: 456; Name: Marcos
    

    本质上,new 是在创建一个由this 表示的新对象,您必须返回this 才能引用创建的对象。事实上,如果您在创建对象之前不使用new,您最终会引用this 的全局实例。请尝试以下操作:

    var nperson = new NewPerson(456, "Marcos");
    this.name = "Hello";
    nperson.print();
    
    var nperson1 = NewPerson(456, "Marcos");
    this.name = "Hello";
    nperson1.print();
    

    您将看到第一个输出将如预期的那样:

    New Person Id: 456; Name: Marcos
    

    但第二个将接收对this.name 所做的更改:

    New Person Id: 456; Name: Hello
    

    Here is the JSFiddle.

    【讨论】:

      猜你喜欢
      • 2016-12-22
      • 2021-10-10
      • 1970-01-01
      • 2022-01-15
      • 2011-05-12
      • 1970-01-01
      • 1970-01-01
      • 2013-10-31
      • 2014-12-07
      相关资源
      最近更新 更多