为什么JavaScript中这些基于闭包的私有变量不起作用

时间:2013-05-12 11:23:52

标签: javascript oop closures

我正在尝试在JS中模仿OOP行为。我想在函数id中拥有(私有)变量:namePerson。对于这个函数,我传递的参数用于初始化(私有)变量。然后我返回具有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,因为在调试时我可以看到值悬停在它们上方时设置的值,但是Locals面板确实没有显示它们已初始化:

Debugging

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

Debugging

这里有什么问题?

5 个答案:

答案 0 :(得分:2)

<强> 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 :(得分:1)

您使用_id_nm以及“公共”实例属性(this.idthis.nm)的本地作用域(“私有”)变量进行了混合。

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

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

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

    ...
};

答案 2 :(得分:1)

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

答案 3 :(得分:1)

使用new或返回值。不是两个。

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

当你从构造函数返回一些东西时,它返回那个值,而不是函数的实例。

您看到执行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);
    };
}

我没有在id上设置namethis属性,因为包含它们没有任何意义。

答案 4 :(得分:0)

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

// 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,但无法返回刚刚创建的对象。相反,您将返回一个字典,该字典引用您在name中创建的idScope 2变量,该变量是您从未设置的,因此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