mor*_*des 184 javascript private-members
有没有办法制作"私有"变量(在构造函数中定义的变量),可用于原型定义的方法?
TestClass = function(){
var privateField = "hello";
this.nonProtoHello = function(){alert(privateField)};
};
TestClass.prototype.prototypeHello = function(){alert(privateField)};
Run Code Online (Sandbox Code Playgroud)
这有效:
t.nonProtoHello()
Run Code Online (Sandbox Code Playgroud)
但这不是:
t.prototypeHello()
Run Code Online (Sandbox Code Playgroud)
我习惯在构造函数中定义我的方法,但由于一些原因,我正在远离它.
Tri*_*ych 183
不,没有办法做到这一点.这基本上是反过来的范围.
构造函数中定义的方法可以访问私有变量,因为所有函数都可以访问定义它们的作用域.
在原型上定义的方法未在构造函数的范围内定义,并且无法访问构造函数的局部变量.
您仍然可以拥有私有变量,但是如果您希望在原型上定义的方法可以访问它们,则应该在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()); };
Run Code Online (Sandbox Code Playgroud)
Sco*_*pey 61
长话短说,你可以用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;
}());
Run Code Online (Sandbox Code Playgroud)
构造对象的最简单方法是完全避免原型继承.只需在闭包中定义私有变量和公共函数,所有公共方法都可以私有访问变量.
在JavaScript中,原型继承主要是一种优化.它允许多个实例共享原型方法,而不是每个实例都有自己的方法.
缺点是,每次调用原型函数时this
,唯一不同的是它.
因此,任何私人领域都必须是可以访问的this
,这意味着它们将是公开的.所以我们坚持使用_private
字段的命名约定.
我认为你不应该把闭包变量和原型方法混在一起.你应该使用其中一个.
使用闭包访问私有变量时,原型方法无法访问变量.因此,您必须将闭包暴露在外this
,这意味着您以某种方式公开它.这种方法几乎没有什么好处.
对于非常简单的对象,只需使用带闭包的普通对象即可.
如果你需要原型继承 - 继承,性能等 - 那么坚持使用"_private"命名约定,而不用担心闭包.
我不明白为什么JS开发人员努力使字段真正私有化.
Mim*_*ght 31
当我读到这篇文章时,这听起来像是一个艰难的挑战,所以我决定想办法.我想出的是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);
Run Code Online (Sandbox Code Playgroud)
有很多情况下这就足够了,就像你想拥有像实例之间共享的事件名这样的常量值.但实际上,它们就像私有静态变量一样.
如果您绝对需要从原型中定义的方法中访问私有命名空间中的变量,则可以尝试此模式.
var PrivateNamespaceClass = (function() { // immediate function
var instance = 0, // counts the number of instances
defaultName = "Default Name",
p = []; // an array of private objects
// create the constructor
function PrivateNamespaceClass() {
// Increment the instance count and save it to the instance.
// This will become your key to your private space.
this.i = instance++;
// Create a new object in the private space.
p[this.i] = {};
// Define properties or methods in the private space.
p[this.i].name = defaultName;
console.log("New instance " + this.i);
}
PrivateNamespaceClass.prototype.getPrivateName = function() {
// It has access to the private space and it's children!
return p[this.i].name;
};
PrivateNamespaceClass.prototype.setPrivateName = function(value) {
// Because you use the instance number assigned to the object (this.i)
// as a key, the values set will not change in other instances.
p[this.i].name = value;
return "Set " + p[this.i].name;
};
return PrivateNamespaceClass;
})();
var a = new PrivateNamespaceClass();
console.log(a.getPrivateName()); // Default Name
var b = new PrivateNamespaceClass();
console.log(b.getPrivateName()); // Default Name
console.log(a.setPrivateName("A"));
console.log(b.setPrivateName("B"));
console.log(a.getPrivateName()); // A
console.log(b.getPrivateName()); // B
// private objects are not accessible outside the PrivateNamespaceClass function
console.log(a.p);
// the prototype functions are not re-created for each instance
// and instanceof still works
console.log(a.getPrivateName === b.getPrivateName);
console.log(a instanceof PrivateNamespaceClass);
console.log(b instanceof PrivateNamespaceClass);
Run Code Online (Sandbox Code Playgroud)
我喜欢任何通过这种方式看到错误的人的反馈.
Jas*_*n S 18
请参阅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); }
Run Code Online (Sandbox Code Playgroud)
用例:
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
Run Code Online (Sandbox Code Playgroud)
小智 15
我建议将"在构造函数中进行原型赋值"描述为Javascript反模式可能是个好主意.想一想.风险太大了.
你在创建第二个对象(即b)时实际做的是为所有使用该原型的对象重新定义原型函数.这将有效地重置示例中对象a的值.如果你想要一个共享变量并且你碰巧事先创建了所有的对象实例,那么它会起作用,但它感觉风险太大了.
我发现最近我正在处理的一些Javascript中的一个错误是由于这种确切的反模式.它试图在正在创建的特定对象上设置拖放处理程序,而是为所有实例执行此操作.不好.
Doug Crockford的解决方案是最好的.
Tim*_*Tim 10
@Kai
那不行.如果你这样做
var t2 = new TestClass();
Run Code Online (Sandbox Code Playgroud)
然后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;
}());
Run Code Online (Sandbox Code Playgroud)
是的,这是可能的.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;
}
Run Code Online (Sandbox Code Playgroud)
...
阅读完整的故事:
小智 5
您实际上可以通过使用访问者验证来实现:
(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());
Run Code Online (Sandbox Code Playgroud)
这个例子来自我关于原型函数和私有数据的帖子,并在此有更详细的解释。
在目前的JavaScript,我相当肯定,有一个和唯一一个有这样私有状态,从接近原型的功能,而无需添加任何公众来this
。答案是使用“弱映射”模式。
总结一下:Person
该类有一个弱映射,其中键是 Person 的实例,值是用于私有存储的普通对象。
这是一个功能齐全的示例:(在http://jsfiddle.net/ScottRippey/BLNVr/播放)
var Person = (function() {
var _ = weakMap();
// Now, _(this) returns an object, used for private storage.
var Person = function(first, last) {
// Assign private storage:
_(this).firstName = first;
_(this).lastName = last;
}
Person.prototype = {
fullName: function() {
// Retrieve private storage:
return _(this).firstName + _(this).lastName;
},
firstName: function() {
return _(this).firstName;
},
destroy: function() {
// Free up the private storage:
_(this, true);
}
};
return Person;
})();
function weakMap() {
var instances=[], values=[];
return function(instance, destroy) {
var index = instances.indexOf(instance);
if (destroy) {
// Delete the private state:
instances.splice(index, 1);
return values.splice(index, 1)[0];
} else if (index === -1) {
// Create the private state:
instances.push(instance);
values.push({});
return values[values.length - 1];
} else {
// Return the private state:
return values[index];
}
};
}
Run Code Online (Sandbox Code Playgroud)
就像我说的,这确实是实现所有 3 个部分的唯一方法。
但是,有两个警告。首先,这会降低性能——每次访问私有数据时,都是一个O(n)
操作,n
实例数在哪里。因此,如果您有大量实例,您不会想要这样做。其次,当你完成一个实例时,你必须调用destroy
; 否则,实例和数据将不会被垃圾收集,最终会导致内存泄漏。
这就是为什么我最初的回答“你不应该”是我想要坚持的。
归档时间: |
|
查看次数: |
110930 次 |
最近记录: |