有没有办法制作"私有"变量(在构造函数中定义的变量),可用于原型定义的方法?
TestClass = function(){ var privateField = "hello"; this.nonProtoHello = function(){alert(privateField)}; }; TestClass.prototype.prototypeHello = function(){alert(privateField)};
这有效:
t.nonProtoHello()
但这不是:
t.prototypeHello()
我习惯在构造函数中定义我的方法,但由于一些原因,我正在远离它.
不,没有办法做到这一点.这基本上是反过来的范围.
构造函数中定义的方法可以访问私有变量,因为所有函数都可以访问定义它们的作用域.
在原型上定义的方法未在构造函数的范围内定义,并且无法访问构造函数的局部变量.
您仍然可以拥有私有变量,但是如果您希望在原型上定义的方法可以访问它们,则应该在this
对象上定义getter和setter ,原型方法(以及其他所有内容)都可以访问它们.例如:
function Person(name, secret) { // public this.name = name; // private var secret = secret; // public methods have access to private members this.setSecret = function(s) { secret = s; } this.getSecret = function() { return secret; } } // Must use getters/setters Person.prototype.spillSecret = function() { alert(this.getSecret()); };
长话短说,你可以用new Symbol
来创建私有字段.
这是一个很好的描述:https://curiosity-driven.org/private-properties-in-javascript
例:
var Person = (function() { // Only Person can access nameSymbol var nameSymbol = Symbol('name'); function Person(name) { this[nameSymbol] = name; } Person.prototype.getName = function() { return this[nameSymbol]; }; return Person; }());对于所有使用ES5的现代浏览器:
构造对象的最简单方法是完全避免原型继承.只需在闭包中定义私有变量和公共函数,所有公共方法都可以私有访问变量.
在JavaScript中,原型继承主要是一种优化.它允许多个实例共享原型方法,而不是每个实例都有自己的方法.
缺点是,每次调用原型函数时this
,唯一不同的是它.
因此,任何私人领域都必须是可以访问的this
,这意味着它们将是公开的.所以我们坚持使用_private
字段的命名约定.
我认为你不应该把闭包变量和原型方法混在一起.你应该使用其中一个.
使用闭包访问私有变量时,原型方法无法访问变量.因此,您必须将闭包暴露在外this
,这意味着您以某种方式公开它.这种方法几乎没有什么好处.
对于非常简单的对象,只需使用带闭包的普通对象即可.
如果你需要原型继承 - 继承,性能等 - 那么坚持使用"_private"命名约定,而不用担心闭包.
我不明白为什么JS开发人员努力使字段真正私有化.
当我读到这篇文章时,这听起来像是一个艰难的挑战,所以我决定想办法.我想出的是CRAAAAZY,但它完全有效.
首先,我尝试在立即函数中定义类,以便您可以访问该函数的某些私有属性.这有效并且允许您获取一些私有数据,但是,如果您尝试设置私有数据,您很快就会发现所有对象将共享相同的值.
var SharedPrivateClass = (function() { // use immediate function
// our private data
var private = "Default";
// create the constructor
function SharedPrivateClass() {}
// add to the prototype
SharedPrivateClass.prototype.getPrivate = function() {
// It has access to private vars from the immediate function!
return private;
};
SharedPrivateClass.prototype.setPrivate = function(value) {
private = value;
};
return SharedPrivateClass;
})();
var a = new SharedPrivateClass();
console.log("a:", a.getPrivate()); // "a: Default"
var b = new SharedPrivateClass();
console.log("b:", b.getPrivate()); // "b: Default"
a.setPrivate("foo"); // a Sets private to "foo"
console.log("a:", a.getPrivate()); // "a: foo"
console.log("b:", b.getPrivate()); // oh no, b.getPrivate() is "foo"!
console.log(a.hasOwnProperty("getPrivate")); // false. belongs to the prototype
console.log(a.private); // undefined
// getPrivate() is only created once and instanceof still works
console.log(a.getPrivate === b.getPrivate);
console.log(a instanceof SharedPrivateClass);
console.log(b instanceof SharedPrivateClass);
请参阅Doug Crockford的页面.您必须间接地使用可以访问私有变量范围的内容.
另一个例子:
Incrementer = function(init) { var counter = init || 0; // "counter" is a private variable this._increment = function() { return counter++; } this._set = function(x) { counter = x; } } Incrementer.prototype.increment = function() { return this._increment(); } Incrementer.prototype.set = function(x) { return this._set(x); }
用例:
js>i = new Incrementer(100); [object Object] js>i.increment() 100 js>i.increment() 101 js>i.increment() 102 js>i.increment() 103 js>i.set(-44) js>i.increment() -44 js>i.increment() -43 js>i.increment() -42
我建议将"在构造函数中进行原型赋值"描述为Javascript反模式可能是个好主意.想一想.风险太大了.
你在创建第二个对象(即b)时实际做的是为所有使用该原型的对象重新定义原型函数.这将有效地重置示例中对象a的值.如果你想要一个共享变量并且你碰巧事先创建了所有的对象实例,那么它会起作用,但它感觉风险太大了.
我发现最近我正在处理的一些Javascript中的一个错误是由于这种确切的反模式.它试图在正在创建的特定对象上设置拖放处理程序,而是为所有实例执行此操作.不好.
Doug Crockford的解决方案是最好的.
@Kai
那不行.如果你这样做
var t2 = new TestClass();
然后t2.prototypeHello
将访问t的私人部分.
@AnglesCrimes
示例代码工作正常,但它实际上创建了一个由所有实例共享的"静态"私有成员.它可能不是摩根代码寻找的解决方案.
到目前为止,我还没有找到一种简单而干净的方法来实现这一点,而无需引入私有哈希和额外的清理功能.可以在一定程度上模拟私有成员函数:
(function() { function Foo() { ... } Foo.prototype.bar = function() { privateFoo.call(this, blah); }; function privateFoo(blah) { // scoped to the instance by passing this to call } window.Foo = Foo; }());
是的,这是可能的.PPF设计模式正好解决了这个问题.
PPF代表私有原型功能.基本PPF解决了这些问题:
原型函数可以访问私有实例数据.
原型功能可以是私有的.
首先,只需:
将您想要从原型函数访问的所有私有实例变量放在一个单独的数据容器中,并且
将对数据容器的引用作为参数传递给所有原型函数.
就这么简单.例如:
// Helper class to store private data. function Data() {}; // Object constructor function Point(x, y) { // container for private vars: all private vars go here // we want x, y be changeable via methods only var data = new Data; data.x = x; data.y = y; ... } // Prototype functions now have access to private instance data Point.prototype.getX = function(data) { return data.x; } Point.prototype.getY = function(data) { return data.y; }
...
阅读完整的故事:
PPF设计模式
您实际上可以通过使用访问者验证来实现:
(function(key, global) { // Creates a private data accessor function. function _(pData) { return function(aKey) { return aKey === key && pData; }; } // Private data accessor verifier. Verifies by making sure that the string // version of the function looks normal and that the toString function hasn't // been modified. NOTE: Verification can be duped if the rogue code replaces // Function.prototype.toString before this closure executes. function $(me) { if(me._ + '' == _asString && me._.toString === _toString) { return me._(key); } } var _asString = _({}) + '', _toString = _.toString; // Creates a Person class. var PersonPrototype = (global.Person = function(firstName, lastName) { this._ = _({ firstName : firstName, lastName : lastName }); }).prototype; PersonPrototype.getName = function() { var pData = $(this); return pData.firstName + ' ' + pData.lastName; }; PersonPrototype.setFirstName = function(firstName) { var pData = $(this); pData.firstName = firstName; return this; }; PersonPrototype.setLastName = function(lastName) { var pData = $(this); pData.lastName = lastName; return this; }; })({}, this); var chris = new Person('Chris', 'West'); alert(chris.setFirstName('Christopher').setLastName('Webber').getName());
这个例子来自我关于原型函数和私有数据的帖子,并在此有更详细的解释。