构造函数中的原型闭包
prototype closure in the constructor
更新:
这种实现非常糟糕,我已经删除了这个答案。
我刚刚回答了这个问题。OP要求提供一个只能通过原型方法访问的私有成员的解决方案。对于我的回答,我不建议这样做,而是提出了可能的代码。(很抱歉,我对标题没有一个好主意。)
-
代码
function A(prop1) { var myFunc=A.prototype.myFunc; var that=this; A.prototype.myFunc=function () { if (this===that) { alert(prop1); // do something } else { myFunc.call(this); } }; this.retire=function () { that=undefined; }; } A.prototype.myFunc=function () { }; var a1=new A(1); var a2=new A(2); var a3=new A(3); a1.myFunc(); a2.myFunc(); a3.myFunc(); a2.retire(); a1.myFunc(); a2.myFunc(); a3.myFunc(); // ..
正如我们所看到的,如果任何其他原型方法将访问prop1
,则需要重复此模式。我曾经想过使用私有数组来实现它,但这段代码似乎要短得多。
但也有不好的地方:
- 它需要一个额外的函数来确保
that
不引用this
A.prototype.myFunc
是随着后来的对象创建而成长(更深层次)的- 由于每个
var myFunc
仍然被A.prototype.myFunc
引用,因此即使在调用retire
并清除对对象的所有外部引用之后,它也可能在gc到来时仍然有效,这是有疑问的 - 我的测试环境有限,很高兴知道这个实现是否存在潜在风险
所以我认为这个问题的答案可能是:
A。一种更可行的方法是在构造函数中更改原型方法,以实现私有成员只能在原型方法中访问。
B。另一种方法可以实现同样的事情,并且代码尽可能简单。
如果能在你的回答中指出我对关闭和垃圾收集的误解,我将不胜感激。
让我们在另一个问题中看看OP的要求:
是否存在模仿"受保护"对象的JavaScript模式属性
答:在某种程度上,最好的方式(在我看来)将它们命名为_myPrivate
BTW-我不想要特权成员函数的模式访问私有属性,因为成员函数仍然平民的
这根本没有意义,OP是否认为A.prototype.myFunc
在A实例上是不可公开访问的?
原型和构造函数的介绍(加上一些私有模式)可以在这里找到
1。它需要一个额外的功能来确保不引用这个
没有变通办法。that
在每个实例化中都被A.prototype.myFunc
捕获,而实例本身就是可以直接访问that
的对象,涉及更多的对象只会让事情变得更糟;CCD_ 13方法已经是解开引用的最简单的方法。
2。A.prototype.myFunc随着之后的对象创建而成长(更深层次)
这只是潜在的风险。A.prototype.myFunc
类似于递归方法,但实际上它不是。它调用上一个myFunc
并检查实例的标识。对于少数实例来说,这不是问题,但对于大量实例来说,增长的深度最终会导致堆栈溢出。
由于实现将需要一个清理机制,所以使调用更深入只会使用一个数组来保存引用,并按需进行清理。
3。由于每个var-myFunc仍然被A.prototype.myFunc引用,因此即使在调用了retired并清除了对对象的所有outter引用之后,它也可能在gc到来时仍然有效,这是有疑问的
事实上,即使gc来收集垃圾,A.prototype.myFunc
捕获的var myFunc
仍然有效。几乎有不可能发布对myFunc
的引用,因为它是一个链式调用,更深调用和浅调用的上下文彼此不可见,因此它们都无法修改跳过一个级别的调用链;未设置的CCD_ 19只会破坏链。任何试图解决这个问题的技巧都会涉及更多的对象,这可能会增加成本,也可能是过度使用。
4。我的测试环境有限,很高兴知道这个实现是否存在潜在风险
作为要点2的答案,当用它创建大量对象时,它可能会导致堆栈溢出。
我倾向于同意人们说"不要打扰私人"的说法,但我认为,如果你真的想要,最好的方法是使用Function#bind
。Crockford的文章没有提到这种方法,可能是因为它早于bind
,用apply
模拟bind
有点麻烦(也可能是因为这是一个额外的开销,不会带来太多好处)。
function classify(fn) {
var privateScope = {}, publicScope = {};
function bindProp(to, target, src, key) {
if (!src.hasOwnProperty(key)) return;
if (!(src[key] && src[key].bind)) return;
target[key] = src[key].bind(to);
}
function ctor() {
var instancePublic = {}, instancePrivate = Object.create(instancePublic);
for (var key in publicScope) {
bindProp(instancePrivate, instancePublic, publicScope, key);
}
for (var key in privateScope) {
instancePrivate[key] = privateScope[key];
}
if (publicScope.hasOwnProperty('constructor'))
publicScope.constructor.apply(instancePrivate, arguments);
return instancePublic;
}
fn.call(publicScope, publicScope, privateScope);
return ctor;
}
这个函数允许您定义一个具有"public"answers"private"作用域的伪类。想法是:
- 公共作用域对象被放置在私有作用域对象的原型链中
- 所有函数都绑定到专用作用域对象
第一次尝试
function classify(fn) {
var privateScope = {}, publicScope = {};
function bindProp(privateScope, scopeObject, key) {
if (!scopeObject.hasOwnProperty(key)) return true;
if (!(scopeObject[key] && scopeObject[key].bind)) return;
privateScope[key] = scopeObject[key].bind(privateScope);
}
function ctor() {
var instancePrivate = Object.create(privateScope),
instancePublic = Object.create(instancePrivate);
for (var key in publicScope) {
console.log(key);
bindProp(instancePrivate, publicScope, key);
}
for (var key in privateScope) {
if (!bindProp(instancePrivate, privateScope, key)
&& !publicScope.hasOwnProperty(key))
instancePublic[key] = void 0;
}
if (publicScope.hasOwnProperty('constructor'))
publicScope.constructor.apply(instancePrivate, arguments);
return instancePublic;
}
fn(publicScope, privateScope);
return ctor;
}
这个版本的原型链颠倒了:
- 私有作用域对象被放置在公共作用域对象的原型链中
- 所有函数都绑定到专用作用域对象
- 任何未被公共成员遮蔽的私有成员都被
undefined
遮蔽
用法
你可以这样使用它:
var Foo = classify(function(pub, priv) {
// constructors are supported but not required
pub.constructor = function(a, b) {
this.a = a;
this.b = b;
};
priv.somePrivateProp = "lol";
priv.doPrivateStuff = function(x, y) {
return x + y;
};
pub.somePublicProp = "rofl";
pub.doStuff = function(x, y) {
return this.doPrivateStuff(x + 1, y + 1) + ' ' + this.somePrivateProp;
};
});
你可以在控制台上玩这个,看看它是否像你预期的那样工作。
var foo = new Foo('abc', 123);
foo.doStuff(3, 5); // "10 lol"
foo.doPrivateStuff(3, 5) // throws TypeError
- 在underscorejs模板中使用闭包
- setTimeout可以与闭包内的函数一起使用吗
- 附加到原型属性的Do函数没有闭包
- 使用闭包共享构造函数参数
- 使用Google闭包编译器包含一个Ecmascript 6类
- 从js引擎的角度来看闭包和构造函数是如何工作的
- for循环中的JavaScript闭包
- Javascript闭包-如何防止内存泄漏
- 就良好实践而言,带闭包的javascript原型是一件好事吗
- 使用闭包创建原型中引用的私有属性
- 不需要的Javascript效果:原型在实例之间共享闭包
- 构造函数中的原型闭包
- 为什么允许通过原型继承访问另一个闭包范围内的私有变量
- 在javascript中,当原型继承比闭包更适合创建对象时
- 为什么我无法访问原型函数(使用闭包)
- javascript中的闭包方法原型
- 在闭包中使用原型模式
- 图表库的闭包或原型模型
- 将Javascript命名空间闭包和原型一起使用会失败
- 是否可以只在函数闭包中应用对象的原型