Based on my understanding of javascript, prototype methods cannot access variables that are private to the scope of the constructor,
var Foo = function() {
var myprivate = 'I am private';
this.mypublic = 'I am public';
}
Foo.prototype = {
alertPublic: function() { alert(this.mypublic); } // will work
alertPrivate: function() { alert(myprivate); } // won't work
}
It makes perfect sense, but is there any way around this that is safe and good practice? Since using prototypes provides a performance benefit in that the member functions are allocated only once, I'd like to achieve a similar functionality while still being able to get to my private variables. I don't think it will work by using a prototype, but is there another pattern, such as a factory method or a closure approach? Something like,
var fooFactory = function() {
var _alertPrivate = function(p) { alert(p); } // bulk of the logic goes here
return function(args) {
var foo = {};
var myprivate = args.someVar;
foo.mypublic = args.someOtherVar;
foo.alertPrivate = function() { _alertPrivate(myprivate); };
return foo;
};
}
var makeFoo = new fooFactory();
var foo = makeFoo(args);
I'm not sure whether a new copy of _alertPrivate is created each time I create a new Foo or if there is any potential performance benefit. The intention is to get a functionality similar to prototyping (inasmuch as it saves memory) while still being able to access private variables.
Thanks.
Based on my understanding of javascript, prototype methods cannot access variables that are private to the scope of the constructor,
var Foo = function() {
var myprivate = 'I am private';
this.mypublic = 'I am public';
}
Foo.prototype = {
alertPublic: function() { alert(this.mypublic); } // will work
alertPrivate: function() { alert(myprivate); } // won't work
}
It makes perfect sense, but is there any way around this that is safe and good practice? Since using prototypes provides a performance benefit in that the member functions are allocated only once, I'd like to achieve a similar functionality while still being able to get to my private variables. I don't think it will work by using a prototype, but is there another pattern, such as a factory method or a closure approach? Something like,
var fooFactory = function() {
var _alertPrivate = function(p) { alert(p); } // bulk of the logic goes here
return function(args) {
var foo = {};
var myprivate = args.someVar;
foo.mypublic = args.someOtherVar;
foo.alertPrivate = function() { _alertPrivate(myprivate); };
return foo;
};
}
var makeFoo = new fooFactory();
var foo = makeFoo(args);
I'm not sure whether a new copy of _alertPrivate is created each time I create a new Foo or if there is any potential performance benefit. The intention is to get a functionality similar to prototyping (inasmuch as it saves memory) while still being able to access private variables.
Thanks.
Share Improve this question edited Dec 27, 2011 at 15:41 Rob W 349k87 gold badges807 silver badges682 bronze badges asked Oct 16, 2011 at 23:52 Sean ThomanSean Thoman 7,4997 gold badges60 silver badges103 bronze badges 4- Are you sure that the performance difference is really a problem? – qerub Commented Oct 18, 2011 at 9:55
- 2 @Qerub, It can be. Typically the rule is that if you need to create a ton of instances, its better to use a prototype since each instance refers back to the functions in the prototype which are created only once. I have benchmarked it and there is a significant performance benefit to using a prototype. There is also the obvious reason of wanting to enable inheritance. – Sean Thoman Commented Oct 18, 2011 at 16:47
- At the end of the following post is a link to creating protected members without creating a lot of closures each time you create an instance: stackoverflow./questions/16063394/… – HMR Commented Jul 18, 2014 at 3:23
- This stack overflow answer shows how you can solve this problem with ES6's WeakMap inside a closure. And if you need it to support old browsers, there there are several polyfills available for WeakMap. – PHP Guru Commented Dec 23, 2024 at 0:00
3 Answers
Reset to default 4I have e up with the following pattern to address this issue, atleast for now. What I needed was a privileged setter so that a private variable could be changed from inside certain prototype functions but not from anywhere else:
var Foo = (function() {
// the bulk of the objects behavior goes here and is created once
var functions = {
update: function(a) {
a['privateVar'] = "Private variable set from the prototype";
}
};
// the objects prototype, also created once
var proto = {
Update: function() {
this.caller('update');
}
};
// special function to get private vars into scope
var hoist = function(accessor) {
return function(key) {
return functions[key](accessor());
}
}
// the constructor itself
var foo = function foo() {
var state = {
privateVar: "Private variable set in constructor",
// put more private vars here
}
this.caller = hoist(function(){
return state;
});
}
// assign the prototype
foo.prototype = proto;
// return the constructor
return foo;
})();
Basically a pointer to the objects internal state is hoisted to its prototype via a closure over a simple accessor function() { return state; }. Using the 'caller' function on any given instance allows you to call functions which are created only once but can still refer to the private state held in that instance. Its also important to note that no functions outside of the prototype could ever access the privileged accessor, since the 'caller' only accepts a key that refers back to the predefined functions which are in scope.
Here are some benchmarks of this method to see how it pares to pure prototyping. These figures represent creating 80,000 instances of the object in a loop (note the object used for benchmarking is more plex than the one above, which was just for simplification purposes):
CHROME:
Closure Only - 2172ms
Prototyping (above way) - 822ms
Prototyping (std way) - 751ms
FIREFOX:
Closure Only - 1528ms
Prototyping (above way) - 971ms
Prototyping (std way) - 752ms
As you can see the method is almost as fast as normal prototyping, and definitely faster than just using a normal closure that copies functions along with the instance.
I found Sean Thoman's answer very helpful (though hard to understand at first).
It didn't look like the public setter could accept a value for privateVar
so I made a few tweaks:
Change update
in functions
:
update: function(st, newVal) {
st['privateVar'] = newVal;
}
Change Update
in the proto
:
Update: function(newVal) {
this.caller('update', newVal);
}
Change hoist
:
var hoist = function(accessor) {
return function(key) {
// we can't slice arguments directly because it's not a real array
var args_tail = Array.prototype.slice.call(arguments, 1);
return functions[key].apply(functions[key], [accessor()].concat(args_tail));
}
}
What you are asking for is possible, although there will always be a tradeoff between performance (in speed or memory) and functionality.
In JavaScript, it is possible to achieve private per-instance state, with normal prototype methods (and with no centralized, leaking, field storage).
Check the article I wrote about the technique: http://www.codeproject./KB/ajax/SafeFactoryPattern.aspx
Or go directly to the source code in: https://github./dcleao/private-state.
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744988896a4604779.html
评论列表(0条)