如何使用Object.create()创建具有私有成员的对象,而不是使用new关键字

7

编辑:最终我从Bergi的答案中弄明白了。

感谢Bergi。

pubPrivExample = (function () {
    return {
        init : function () {
            var private;

            this.setPrivate = function (p) {
                private = p;
            };
            this.getPrivate = function () {
                return private;
            };
        },

        public : "This is public\n"
    };
}());

var a;

a = Object.create(pubPrivExample);
a.init();
a.setPrivate("This is private");

document.write(a.getPrivate());

编辑:看起来对于我的问题的回答离题了。我真的不感兴趣使用工厂,而且宁愿不使用。我的问题是关于私有状态的。从Bergi的回答和评论中,我认为我可以总结一些东西。

继续...

编辑:Bergi已经开始回答下面的问题,但是遗漏了最重要的部分——私有状态。

我已经有时间思考这个想法,但是仍然无法在不使用任何类型的工厂的情况下使用Object.create()实现私有状态。但是我希望是错误的,Bergi暗示了一个解决方法...随意以Bergi的答案作为起点。

原始内容:我想避免在JavaScript中使用new,这使我走上了一条奇怪的路。我想要私有对象成员,但我不想放弃Object.create()

以下是代码:

var trackQueue = {};

trackQueue.factory = function () {
    var that, queue;
    that = this;
    queue = [];

    that.push = function (item) {
        queue.push(item);
    };

    that.work = function () {
        document.write(queue + "<br />");
    };

    return {
        work : that.work,
        push : that.push
    };        
};

var a = Object.create( trackQueue.factory() );
a.push("a");
a.push("b");
a.push("c");

var b = Object.create( trackQueue.factory() );
b.push("d");
b.push("e");
b.push("f");

a.work();
b.work();

还有一个 jsfiddle:

http://jsfiddle.net/dsjbirch/Wj6cp/10/

如果把 factory 方法改名为 init,是否更符合惯用语法 / 更恰当?

这样做疯狂吗?请谅解 - javascript 不是我的母语。


2
为什么你不想使用 new - user1106925
2个回答

2

是的,原型上的init方法可能更合适:

var proto = {
    init: function(args) {
        // setting up private-scoped vars,
        var example = args;
        // privileged methods
        this.accessPrivate = function(){ return example; };
        // and other stuff
        this.public = 5;
    },
    prop: "defaultvalue",
    ...
}

var instance = Object.create(proto);
instance.init();

然而,使用经典构造函数和new关键字结合的方式完美地结合了Object.create和init调用,没有任何理由不这样做。

请注意,您在没有任何用处的情况下使用了Object.create。您的工厂模式(完全有效)返回良好的对象。没有必要为每个继承它们的对象创建新对象。只需执行以下操作:

var instance = trackQueue.factory();

如果您喜欢“create”方法名称的音响,您可以为工厂使用更符合习惯用法的名称:
trackQueueFactory.create = function(args) {...};

编辑:你将工厂模式和原型继承相结合的想法并不完全错误。然而,所有已制造对象都要继承的原型对象需要是静态的,而不是在每次调用时创建新对象。你的代码可能如下所示:

var factory = {
    proto: {
        ...
    },
    create: function(args) {
        var product = Object.create(this.proto);
        // set up private vars scoped to the create function
        // privileged methods
        product.doSomethingSpecial = function(){ ... };
        // and other stuff
    }
};

var a = factory.create(...);

在你的示例中,我如何设置init函数中的私有作用域变量? - bluekeys
和平常一样:使用var关键字或函数声明,它们将被限定在init函数的作用域内。 - Bergi

2
我认为这是实现您需求的明确途径:
var personFactory = function(id, name, age){
    var _id = id;
    var _name = name;
    var _age = age;

    var personPrototype = {
        getId: function(){
            return _id;
        },
        setId: function(id){
            _id = id;
        },
        getName: function(){
            return _name;
        },
        setName: function(name){
            _name = name;
        },
        getAge: function(){
            return _age; 
        },
        setAge: function(age){
            _age = age;
        },
        work: function(){
            document.write(this.toString());
        },
        toString: function(){
            return "Id: " + _id + " - Name: " + _name + " - Age: " + _age;
        }
    };

    return Object.create(personPrototype);
};

使用方法:

var renato = personFactory(1, "Renato Gama", 25);
console.log(renato.getName()); //logs "Renato Gama"
renato.setName("Renato Mendonça da Gama");
console.log(renato.getName()); //logs "Renato Mendonça da Gama"

如果我没有错的话,这是模块模式的一个用法。请参考这篇文章,以获得更好的解释。同时,这篇关于该主题的文章也很不错。


不,你错了。你和 OP 犯了同样的错误:在私有作用域对象上使用 Object.create 完全没有必要。 - Bergi
我不太明白你的意思,能否再解释一下?我不明白为什么这被认为是一个错误。我甚至认为你的第二个代码块与我写的那个类似,只是你的原型是工厂的公共成员。如果你的工厂对象除了create()函数还有其他公共函数,我同意你的方法。 - Renato Gama
为什么不直接返回对象字面量(“personPrototype”)?在你的代码中,Object.create有什么用处? - Bergi
目的是返回一个已经从工厂中设置好属性的实例化对象(类似于构造方法)。换句话说,封装初始化逻辑。 - Renato Gama
是的,当返回“personPrototype”对象时,它确实会这样做。你知道Object.create是什么吗? - Bergi
很好的答案,就像其他人建议的那样,我会简单地删除Object.create,并返回personPrototype。 - Dave Voyles

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接