2014-03-28 67 views
3

我喜欢闭包,因为你可以从它们制作一个API,但它很糟糕,你不能拥有多个实例。闭包和多个实例

http://jsfiddle.net/LxCJe/1/

var Person = (function() { 

    // private properties and methods 
    var Constr, name; 

    // public API 
    Constr = function (n) { 
     name = n; 
    }; 

    Constr.prototype.sayName = function(){return name;}; 

    return Constr; 
}()); 


var person1 = new Person('Foo'); 
var person2 = new Person('Bar'); //New values will overwrite the name because of the closure. 

console.log(person1.sayName()); //Bar 
console.log(person2.sayName()); //Bar 

是否有其他办法可以使用原型来访问私有成员和创建不同的实例?

+2

他们是私人,但不是唯一的每个实例。不要担心JS中的封装,可以使用'this._name',所以下划线是一个意思是“私有”的约定。 – elclanrs

+1

原型方法是共享的,所以它们不能以每个对象为基础访问变量。 @elclanrs:在大多数情况下,解决方案应该绰绰有余。否则,你将放弃原型方法,并为每个实例分配自己的方法。 –

+0

@ cookiemonster - 但公共方法可以通过特权方法访问私有成员,所以它不是世界的尽头,只是更低效。 :-) – RobG

回答

2

如果你真的想利用构造函数和成员为私有,那么你可以做这样的

var Person = function(my_name) { 

    // private properties and methods 
    var Constr, name; 

    // public API 
    Constr = function(n) { 
     name = n; 
    }; 

    Constr.prototype.sayName = function() { 
     return name; 
    }; 

    return new Constr(my_name); 
}; 

注:

  1. 但是,这是没有效率的,因为每次创建Person的对象时,我们必须创建Constr构造函数。

  2. 它使从Constr/Person不可能继承,如Constr不能从外部访问和Person原型是空的。

    console.log(Person.prototype); // {} 
    

我认为,不是所有的变量将是你的私人类。所以,你可以有私有成员,这样

var Person = function(my_name) { 

    // private properties and methods 
    var name = my_name; 

    // public API 
    this.getName = function() { 
     return name; 
    } 

    this.setName = function(newName) { 
     name = newName; 
    } 
}; 

Person.prototype.printName = function() { 
    console.log(this.getName()); 
} 

var person1 = new Person('Foo'); 
var person2 = new Person('Bar'); 

console.log(person1.getName()); // Foo 
console.log(person2.getName()); // Bar 
console.log(Person.prototype); // { printName: [Function] } 
person1.printName(); 
person2.printName(); 
+0

是的,通常使用特权方法来获取和设置私有成员(以及其他东西)的值,以便它们可用于继承的方法。但正如elclanrs所说,只要你受到纪律处分(有时候并不总是这样),有时候最终不值得付出努力。 – RobG

+0

了解后者,似乎没有其他办法。感谢您展示非正统的选项,尽管如此! – Tek

0

你可以在这里降低构造的数量,并创建一个新的公共接口每次new Person(name)被称为:

function Person(name) 
{ 
    // return public api that closes over 'name' 
    return { 
     sayName: function() { 
      return name; 
     } 
    }; 
} 

您返回的公共接口将不再是Person的一个实例,对于这种特殊方法应该记住这一点。

1

我认为未来的方式是Object.defineProperties,它与通常兼容HTML5的浏览器兼容(IE9 +,最显着的是;请参阅es5-shim以获得最佳的回填效果)。

有了这一点,你可以定义只读干将,看起来就像属性(不是函数),不要毁了原型继承或创建一个新的构造函数每次:(JSFiddle here

// name is read-only, but only modern browsers (ie 9+) 
function Person(attributes) { 
    Object.defineProperties(this, { 
     name: {value: attributes.name} 
    }) 
} 

person1 = new Person({name: 'Foo'}); 
person2 = new Person({name: 'Bar'}); 

console.log(person1.name) // Foo 
console.log(person2.name) // Bar 

或者,类似于别人的评论,你可以做以下为更好的浏览器兼容性,同时保持原型正确性和只读API:

// read-only-ish, but better browser compatibility 
function Person(attributes) { 
    this._name = attributes.name 
} 
Person.prototype.name = function() { return this._name } 

person1 = new Person({name: 'Foo'}); 
person2 = new Person({name: 'Bar'}); 

console.log(person1.name()) // Foo 
console.log(person2.name()) // Bar 
+0

太棒了。你如何使用Person中的Object.defineProperties来访问/覆盖名字? – Tek

+0

@Tek取决于你的意图。你可以再次调用Object.defineProperties(没有尝试,只是有意义),或者更好,但如果你只是想要一个标准的getter/setter,你可以在'{value:attributes.name}中加入'writable:true'' – Woahdae