How to set javascript private variables in constructor?
Say I have a javascript function/class called Foo
and it has a property called bar
. I want the value of bar
to be supplied when the class is instantiated, e.g:
var myFoo = new Foo(5);
would set myFoo.bar
to 5.
If I make bar
a public variable, then this works, e.g:
function Foo(bar)
{
this.bar = bar;
}
But if I want to make it private, e.g:
function Foo(bar)
{
var bar;
}开发者_StackOverflow中文版
Then how would I set the value of the private variable bar
such that its available to all internal functions of foo
?
One of the best tutorials on private and protected access in javascript is here: http://javascript.crockford.com/private.html.
function Foo(a) {
var bar = a; // private instance data
this.getBar = function() {return(bar);} // methods with access to private variable
this.setBar = function(a) {bar = a;}
}
var x = new Foo(3);
var y = x.getBar(); // 3
x.setBar(12);
var z = x.bar; // not allowed (x has no public property named "bar")
You have to put all functions that need to access the private variable inside the constructor:
function Foo(bar)
{
//bar is inside a closure now, only these functions can access it
this.setBar = function() {bar = 5;}
this.getBar = function() {return bar;}
//Other functions
}
var myFoo = new Foo(5);
myFoo.bar; //Undefined, cannot access variable closure
myFoo.getBar(); //Works, returns 5
function Foo(b)
{
var bar = b;
this.setBar = function(x){
bar = x;
}
this.alertBar = function(){
alert(bar);
}
}
var test = new Foo(10);
alert(test.bar); // Alerts undefined
test.alertBar(); // Alerts 10
One way I can think of is to use a closure that's assigned to a name and returns a new object. You would pass in any arguments to the constructor through the call to the closure. That would end up being something like the following:
var fooFactory = function (a, b) {
var c = 5,
d = 6,
foo;
foo = function (a, b) {
this.a = a;
this.b = b;
this.bar();
}
foo.prototype.bar = function () {
//do something with c and d
this.c = c + d;
}
foo.prototype.getC = function () {
return c;
}
foo.prototype.getD = function () {
return d;
}
return new foo(a, b);
};
This way, a and b are always declared uniquely. You would then construct your object like so:
var obj = fooFactory(1, 2);
//obj contains new object: { a: 1, b: 2, c: 11 }
console.log(obj.getC());
//returns 5
If you are willing to use ES2015 classes,
with ESNext, you can use Javascript private variables like this:
class Foo {
#bar = '';
constructor(val){
this.#bar = val;
}
otherFn(){
console.log(this.#bar);
}
}
Private field #bar is not accessible outside Foo class.
In ES6+ terms the proper way to do this is as shown in the @Nitin Jadhav's answer. However if for some reason you would like to stick with the good old constructor functions it could be achieved like;
function Foo(val){
function Construct(){};
Construct.prototype = { set bar(_){} // you may throw an error here
, get bar(){return val;}
};
return new Construct();
};
So two things happen here.
- You don't populate the instances of
Foo
with properties like in the accepted answer. - Unlike the
Private Class Fields
abstraction you are free to throw an error or not when someone tries to access the private variable through thesetter
.
Perhaps you would like a private field of the instance itself instead of accessing it through the prototype. Then you may do like;
function Foo(val){
Object.defineProperty(this,"bar",{ set: function(){}
, get: function(){return val;}
});
};
I recently had a similar issue but wanted to use accessor properties also. Below is a Foo(Bar) example based on what I came up with for a solution. This example is trivial but can easily be expanded upon using more complex get/set functions.
function Foo(Bar){
Object.defineProperty(this,"bar",{get:function(){return Bar},set:function(val){Bar=val}});
}
x=new Foo(3);
y=x.bar; //3
x.bar++; //x.bar==4
精彩评论