从原型定义的函数访问私有成员变量
有什么方法可以使“私有”变量(在构造函数中定义的变量)可用于原型定义的方法吗?
TestClass = function(){
var privateField = "hello";
this.nonProtoHello = function(){alert(privateField)};
};
TestClass.prototype.prototypeHello = function(){alert(privateField)};
这可行:
t.nonProtoHello()
但这不行:
t.prototypeHello()
我习惯在构造函数内定义我的方法,但出于几个原因我不再这样做。
Is there any way to make “private” variables (those defined in the constructor), available to prototype-defined methods?
TestClass = function(){
var privateField = "hello";
this.nonProtoHello = function(){alert(privateField)};
};
TestClass.prototype.prototypeHello = function(){alert(privateField)};
This works:
t.nonProtoHello()
But this doesn’t:
t.prototypeHello()
I’m used to defining my methods inside the constructor, but am moving away from that for a couple reasons.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(24)
不,没有办法做到这一点。 这本质上是反向范围界定。
构造函数内定义的方法可以访问私有变量,因为所有函数都可以访问定义它们的范围。
在原型上定义的方法不在构造函数的范围内定义,并且无法访问构造函数的局部变量。
您仍然可以拥有私有变量,但如果您希望原型上定义的方法能够访问它们,则应该在
this
对象上定义 getter 和 setter,这是原型方法(以及其他所有内容) 将有权访问。 例如:No, there's no way to do it. That would essentially be scoping in reverse.
Methods defined inside the constructor have access to private variables because all functions have access to the scope in which they were defined.
Methods defined on a prototype are not defined within the scope of the constructor, and will not have access to the constructor's local variables.
You can still have private variables, but if you want methods defined on the prototype to have access to them, you should define getters and setters on the
this
object, which the prototype methods (along with everything else) will have access to. For example:更新:使用 ES6,有一个更好的方法:
长话短说,您可以使用新的
Symbol
来创建私有字段。这是一个很好的描述:https://curiosity-driven.org/private-properties-in-javascript
示例:
对于所有使用 ES5 的现代浏览器:
您可以仅使用闭包
构造对象的最简单方法是完全避免原型继承。
只需在闭包中定义私有变量和公共函数,所有公共方法都可以私有访问这些变量。
或者您可以只使用原型
在 JavaScript 中,原型继承主要是一种优化。 它允许多个实例共享原型方法,而不是每个实例都有自己的方法。
缺点是
this
是每次调用原型函数时唯一不同的东西。因此,任何私有字段都必须可以通过
this
访问,这意味着它们将是公共的。 因此,我们只遵循_private
字段的命名约定。不要费心将闭包与原型混合在一起
我认为你不应该将闭包变量与原型方法混合在一起。 您应该使用其中之一。
当您使用闭包访问私有变量时,原型方法无法访问该变量。 因此,您必须将闭包公开到
this
上,这意味着您以某种方式公开公开它。 这种方法几乎没有什么收获。我该选择哪个?
对于非常简单的对象,只需使用带有闭包的普通对象即可。
如果您需要原型继承(用于继承、性能等),那么请坚持使用“_private”命名约定,并且不要为闭包而烦恼。
我不明白为什么 JS 开发人员如此努力地使字段真正私有。
Update: With ES6, there is a better way:
Long story short, you can use the new
Symbol
to create private fields.Here's a great description: https://curiosity-driven.org/private-properties-in-javascript
Example:
For all modern browsers with ES5:
You can use just Closures
The simplest way to construct objects is to avoid prototypal inheritance altogether.
Just define the private variables and public functions within the closure, and all public methods will have private access to the variables.
Or you can use just Prototypes
In JavaScript, prototypal inheritance is primarily an optimization. It allows multiple instances to share prototype methods, rather than each instance having its own methods.
The drawback is that
this
is the only thing that's different each time a prototypal function is called.Therefore, any private fields must be accessible through
this
, which means they're going to be public. So we just stick to naming conventions for_private
fields.Don't bother mixing Closures with Prototypes
I think you shouldn't mix closure variables with prototype methods. You should use one or the other.
When you use a closure to access a private variable, prototype methods cannot access the variable. So, you have to expose the closure onto
this
, which means that you're exposing it publicly one way or another. There's very little to gain with this approach.Which do I choose?
For really simple objects, just use a plain object with closures.
If you need prototypal inheritance -- for inheritance, performance, etc. -- then stick with the "_private" naming convention, and don't bother with closures.
I don't understand why JS developers try SO hard to make fields truly private.
当我读到这篇文章时,这听起来像是一个艰巨的挑战,所以我决定找出一种方法。 我想出的是CRAAAAZY,但它完全有效。
首先,我尝试在立即函数中定义类,以便您可以访问该函数的一些私有属性。 这有效并允许您获取一些私有数据,但是,如果您尝试设置私有数据,您很快就会发现所有对象将共享相同的值。
在很多情况下,这已经足够了,例如您想要拥有常量值(例如在实例之间共享的事件名称)。 但本质上,它们的作用就像私有静态变量。
如果您绝对需要从原型上定义的方法中访问私有命名空间中的变量,则可以尝试此模式。
我希望任何看到这种方法有错误的人提供一些反馈。
When I read this, it sounded like a tough challenge so I decided to figure out a way. What I came up with was CRAAAAZY but it totally works.
First, I tried defining the class in an immediate function so you'd have access to some of the private properties of that function. This works and allows you to get some private data, however, if you try to set the private data you'll soon find that all the objects will share the same value.
There are plenty of cases where this would be adequate like if you wanted to have constant values like event names that get shared between instances. But essentially, they act like private static variables.
If you absolutely need access to variables in a private namespace from within your methods defined on the prototype, you can try this pattern.
I'd love some feedback from anyone who sees an error with this way of doing it.
请参阅 Doug Crockford 的页面。 您必须使用可以访问私有变量范围的东西来间接完成此操作。
另一个例子:
用例:
see Doug Crockford's page on this. You have to do it indirectly with something that can access the scope of the private variable.
another example:
use case:
我建议将“在构造函数中进行原型赋值”描述为 Javascript 反模式可能是个好主意。 想一想。 这太冒险了。
在创建第二个对象(即 b)时,您实际上所做的是为使用该原型的所有对象重新定义该原型函数。 这将有效地重置示例中对象 a 的值。 如果您想要一个共享变量并且您碰巧预先创建了所有对象实例,那么它会起作用,但感觉风险太大。
我在最近处理的一些 Javascript 中发现了一个错误,这是由于这种反模式造成的。 它试图在正在创建的特定对象上设置拖放处理程序,但改为对所有实例执行此操作。 不好。
道格·克罗克福德的解决方案是最好的。
I suggest it would probably be a good idea to describe "having a prototype assignment in a constructor" as a Javascript anti-pattern. Think about it. It is way too risky.
What you're actually doing there on creation of the second object (i.e. b) is redefining that prototype function for all objects that use that prototype. This will effectively reset the value for object a in your example. It will work if you want a shared variable and if you happen to create all of the object instances up front, but it feels way too risky.
I found a bug in some Javascript I was working on recently that was due to this exact anti-pattern. It was trying to set a drag and drop handler on the particular object being created but was instead doing it for all instances. Not good.
Doug Crockford's solution is the best.
是的,这是可能的。 PPF设计模式正好解决了这个问题。
PPF 代表私有原型函数。 基本 PPF 解决了这些问题:
对于第一个,只需:
就是这么简单。 例如:
...
在这里阅读完整的故事:
PPF 设计模式
Yes, it's possible. PPF design pattern just solves this.
PPF stands for Private Prototype Functions. Basic PPF solves these issues:
For the first, just:
It's that simple. For example:
...
Read the full story here:
PPF Design Pattern
实际上,您可以通过使用访问器验证来实现此目的:
此示例来自我关于原型函数和 私有数据 并在那里有更详细的解释。
You can actually achieve this by using Accessor Verification:
This example comes from my post about Prototypal Functions & Private Data and is explained in more detail there.
在当前的 JavaScript 中,我相当确定有一种并且只有一种方式来拥有私有状态,可以从原型<访问/strong> 函数,无需向
this
添加任何 public 内容。 答案是使用“弱映射”模式。总结一下:Person 类有一个弱映射,其中键是 Person 的实例,值是用于私有存储的普通对象。
这是一个功能齐全的示例:(在 http://jsfiddle.net/ScottRippey/BLNVr/ )
就像我说的,这确实是实现所有 3 个部分的唯一方法。
不过,有两个注意事项。 首先,这会降低性能 - 每次访问私有数据时,都是一个
O(n)
操作,其中n
是实例数。 因此,如果您有大量实例,您将不想这样做。其次,当你完成一个实例时,你必须调用
destroy
; 否则,实例和数据将不会被垃圾收集,并且最终会出现内存泄漏。这就是为什么我想要坚持我原来的答案,“你不应该”。
In current JavaScript, I'm fairly certain that there is one and only one way to have private state, accessible from prototype functions, without adding anything public to
this
. The answer is to use the "weak map" pattern.To sum it up: The
Person
class has a single weak map, where the keys are the instances of Person, and the values are plain objects that are used for private storage.Here is a fully functional example: (play at http://jsfiddle.net/ScottRippey/BLNVr/)
Like I said, this is really the only way to achieve all 3 parts.
There are two caveats, however. First, this costs performance -- every time you access the private data, it's an
O(n)
operation, wheren
is the number of instances. So you won't want to do this if you have a large number of instances.Second, when you're done with an instance, you must call
destroy
; otherwise, the instance and the data will not be garbage collected, and you'll end up with a memory leak.And that's why my original answer, "You shouldn't", is something I'd like to stick to.
有一种更简单的方法,即利用
bind
和call
方法。通过为对象设置私有变量,您可以利用该对象的范围。
示例
此方法并非没有缺点。 由于作用域上下文已被有效覆盖,因此您无法访问
_private
对象之外的内容。 然而,仍然可以访问实例对象的范围也不是不可能的。 您可以将对象的上下文 (this
) 作为第二个参数传递给bind
或call
,以便仍然可以访问原型中的公共值功能。获取公共价值观
There's a simpler way by leveraging the use of
bind
andcall
methods.By setting private variables to an object, you can leverage that object's scope.
Example
This method isn't without drawbacks. Since the scope context is effectively being overridden, you don't have access outside of the
_private
object. However, it isn't impossible though to still give access to the instance object's scope. You can pass in the object's context (this
) as the second argument tobind
orcall
to still have access to it's public values in the prototype function.Accessing public values
尝试一下!
Try it!
这是我在尝试寻找此问题的最简单解决方案时想到的一些东西,也许它对某人有用。 我是 javascript 新手,所以代码很可能存在一些问题。
Here's something I've come up with while trying to find most simple solution for this problem, perhaps it could be useful to someone. I'm new to javascript, so there might well be some issues with the code.
我参加聚会迟到了,但我想我可以做出贡献。 在这里,检查一下:
我将此方法称为访问器模式。 基本思想是我们有一个闭包,闭包内有一个键,并且我们创建一个私有对象(在构造函数中),它可以仅当您拥有密钥时才能访问。
如果您有兴趣,可以在 我的文章。 使用此方法,您可以创建无法在闭包外部访问的每个对象属性。 因此,您可以在构造函数或原型中使用它们,但不能在其他地方使用它们。 我还没有在任何地方看到过这种方法,但我认为它非常强大。
I'm late to the party, but I think I can contribute. Here, check this out:
I call this method accessor pattern. The essential idea is that we have a closure, a key inside the closure, and we create a private object (in the constructor) that can only be accessed if you have the key.
If you are interested, you can read more about this in my article. Using this method, you can create per object properties that cannot be accessed outside of the closure. Therefore, you can use them in constructor or prototype, but not anywhere else. I haven't seen this method used anywhere, but I think it's really powerful.
ES6 WeakMaps
通过使用基于ES6 WeakMaps的简单模式,可以获得可从原型函数访问的私有成员变量。
有关此模式的更详细说明,请参见 这里
ES6 WeakMaps
By using a simple pattern based in ES6 WeakMaps is possible to obtain private member variables, reachable from the prototype functions.
A more detailed explanation of this pattern can be found here
这就是我的想法。
此实现的主要问题是它在每个实例上重新定义原型。
Here's what I came up with.
the main problem with this implementation is that it redefines the prototypes on every instanciation.
有一个非常简单的方法可以做到这一点
JavaScript 原型是黄金。
There is a very simple way to do this
JavaScript prototypes are golden.
我今天遇到了完全相同的问题,在详细阐述了 Scott Rippey 的一流响应之后,我想出了一个非常简单的解决方案(恕我直言),它既与 ES5 兼容又高效,而且也是名称冲突安全的(使用 _private 似乎不安全) 。
使用ringojs和nodejs进行测试。 我很想听听你的意见。
I faced the exact same question today and after elaborating on Scott Rippey first-class response, I came up with a very simple solution (IMHO) that is both compatible with ES5 and efficient, it also is name clash safe (using _private seems unsafe).
Tested with ringojs and nodejs. I'm eager to read your opinion.
我有一个解决方案,但我不确定它是否没有缺陷。
为了使其工作,您必须使用以下结构:
代码如下:
它的工作原理是,它提供了一个实例函数“this.getPrivateFields”来访问“privateFields”私有变量对象,但该函数只会返回定义的主闭包内的“privateFields”对象(也是原型函数)使用“this.getPrivateFields”需要在此闭包内定义)。
使用运行时产生的难以猜测的哈希作为参数,以确保即使在闭包范围之外调用“getPrivateFields”也不会返回“privateFields”对象。
缺点是我们无法在闭包之外使用更多原型函数来扩展 TestClass。
这是一些测试代码:
编辑:使用此方法,还可以“定义”私有函数。
I have one solution, but I am not sure it is without flaws.
For it to work, you have to use the following structure:
Here is the code:
How this works is that it provides an instance function "this.getPrivateFields" to access the "privateFields" private variables object, but this function will only return the "privateFields" object inside the main closure defined (also prototype functions using "this.getPrivateFields" need to be defined inside this closure).
A hash produced during runtime and difficult to be guessed is used as parameters to make sure that even if "getPrivateFields" is called outside the scope of closure will not return the "privateFields" object.
The drawback is that we can not extend TestClass with more prototype functions outside the closure.
Here is some test code:
EDIT: Using this method, it is also possible to "define" private functions.
今天正在研究这个问题,这是我在不使用符号的情况下能找到的唯一解决方案。 最好的事情是它实际上可以完全私有。
该解决方案基于自主开发的模块加载器,该加载器基本上成为私有存储缓存的中介(使用弱映射)。
Was playing around with this today and this was the only solution I could find without using Symbols. Best thing about this is it can actually all be completely private.
The solution is based around a homegrown module loader which basically becomes the mediator for a private storage cache (using a weak map).
我知道自从有人问这个问题以来已经过去了十多年了,但我只是在我的程序员生涯中第n次思考这个问题,并找到了一个可能的解决方案,我不知道我是否完全喜欢。 我以前没有见过这种方法的记录,所以我将其命名为“私有/公共美元模式”或_$ / $ 模式。
该概念使用一个ClassDefinition函数,该函数返回一个构造函数函数,该函数返回一个Interface对象。 该接口的唯一方法是
$
,它接收一个name
参数来调用构造函数对象中的相应函数,在name
之后传递的任何其他参数都会被传递在调用中。全局定义的辅助函数
ClassValues
根据需要将所有字段存储在对象中。 它定义了_$
函数来通过name
访问它们。 这遵循一个简短的获取/设置模式,因此如果传递value
,它将用作新的变量值。全局定义的函数
Interface
采用一个对象和一个Values
对象来返回一个_interface
以及一个函数$
检查obj
以查找以参数name
命名的函数,并使用values
作为范围对象来调用它。 传递给$
的附加参数将在函数调用时传递。在下面的示例中,
ClassX
被分配给ClassDefinition
的结果,即Constructor
函数。构造函数
可以接收任意数量的参数。Interface
是外部代码调用构造函数后获得的内容。尽管您可以在构造函数函数体中定义非原型函数,但在构造函数中使用非原型函数是没有意义的。 所有函数均使用公共美元模式
this.$("functionName"[, param1[, param2 ...]])
进行调用。 私有值通过私有美元模式this._$("valueName"[, replacementValue]);
进行访问。 由于Interface
没有_$
的定义,因此外部对象无法访问这些值。 由于每个原型函数体的this
都设置为函数$
中的values
对象,因此如果直接调用 Constructor 同级函数,将会出现异常; 原型函数体中也需要遵循 _$ / $ 模式。 下面是示例用法。以及控制台输出。
_$ / $ 模式 允许完全原型化的类中的值的完全隐私。 我不知道我是否会使用这个,也不知道它是否有缺陷,但是嘿,这是一个很好的谜题!
I know it has been more than 1 decade since this was was asked, but I just put my thinking on this for the n-th time in my programmer life, and found a possible solution that I don't know if I entirely like yet. I have not seen this methodology documented before, so I will name it the "private/public dollar pattern" or _$ / $ pattern.
The concept uses a ClassDefinition function that returns a Constructor function that returns an Interface object. The interface's only method is
$
which receives aname
argument to invoke the corresponding function in the constructor object, any additional arguments passed aftername
are passed in the invocation.The globally-defined helper function
ClassValues
stores all fields in an object as needed. It defines the_$
function to access them byname
. This follows a short get/set pattern so ifvalue
is passed, it will be used as the new variable value.The globally defined function
Interface
takes an object and aValues
object to return an_interface
with one single function$
that examinesobj
to find a function named after the parametername
and invokes it withvalues
as the scoped object. The additional arguments passed to$
will be passed on the function invocation.In the sample below,
ClassX
is assigned to the result ofClassDefinition
, which is theConstructor
function.Constructor
may receive any number of arguments.Interface
is what external code gets after calling the constructor.There is no point in having non-prototyped functions in
Constructor
, although you could define them in the constructor function body. All functions are called with the public dollar patternthis.$("functionName"[, param1[, param2 ...]])
. The private values are accessed with the private dollar patternthis._$("valueName"[, replacingValue]);
. AsInterface
does not have a definition for_$
, the values cannot be accessed by external objects. Since each prototyped function body'sthis
is set to thevalues
object in function$
, you will get exceptions if you call Constructor sibling functions directly; the _$ / $ pattern needs to be followed in prototyped function bodies too. Below sample usage.And the console output.
The _$ / $ pattern allows full privacy of values in fully-prototyped classes. I don't know if I will ever use this, nor if it has flaws, but hey, it was a good puzzle!
不能把变量放到更高的作用域吗?
Can't you put the variables in a higher scope?
您还可以尝试不直接在原型上添加方法,而是在构造函数上添加方法,如下所示:
You can also try to add method not directly on prototype, but on constructor function like this:
这个怎么样? 使用私有访问器。 只允许您获取变量,但不允许设置它们,具体取决于用例。
How's this? Using an private accessor. Only allows you to get the variables though not to set them, depends on the use case.
您需要更改代码中的 3 处内容:
var privateField = "hello"
替换为this.privateField = "hello"
。privateField
替换为this.privateField
。privateField
替换为this.privateField
。最终代码如下:
You need to change 3 things in your code:
var privateField = "hello"
withthis.privateField = "hello"
.privateField
withthis.privateField
.privateField
withthis.privateField
.The final code would be the following:
您可以在构造函数定义中使用原型赋值。
该变量对于原型添加的方法是可见的,但函数的所有实例都将访问相同的 SHARED 变量。
我希望这有用。
You can use a prototype assignment within the constructor definition.
The variable will be visible to the prototype added method but all the instances of the functions will access the same SHARED variable.
I hope this can be usefull.