I have this class where I have a private property and a public method for access:
Person = function () {
this.Name = "asd";
var _public = new Object();
_public.Name = function (value) {
if (value == undefined) { //Get
return this.Name
} else {
this.Name = value; //Set
}
};
return _public;
};
I want to force the context in _public.Name for access a this.Name.
I know the technique of closure, but I want to see if I can force a context.
I found a technique to do it, extend object Function:
Function.prototype.setScope = function (scope) {
var f = this;
return function () {
f().apply(scope);
}
}
And my class becomes:
Person = function () {
this.Name = "asd";
var _public = new Object();
_public.Name = function (value) {
if (value == undefined) {
return this.Name
} else {
this.Name = value;
}
}.setScope(this);
return _public;
};
So I can force correctly the context, but I can not pass value and can not, however, return this.Name.
Not
just
(No
()afterf.) You want to use theapplyfunction on the functionfobject, not call the functionfand accessapplyon its return value.To also pass on the arguments that your function in
setScopereceives, add this:argumentsis an implicit argument to all functions, which is a pseudo-array of the actual arguments passed to the function at runtime.applyaccepts any array-like thing as its second parameter to specify the arguments to use when calling the underlying function.I’d also have it return the return value:
So
setScopebecomes:Live example
Note that the usual name for this function, and the name it has in the new ECMAScript5 standard, is
bind(Section 15.3.4.5; ECMAScript5’sbindalso lets you curry arguments, which isn’t done by this implementation).setScopeis a particularly unfortunate name, because it doesn’t set the scope, it sets the context.Having said all that, there’s no reason you need
setScopein yourPersonconstructor. You can just do this:Live example
But using
bind(akasetScope) can be useful in places where you don’t want a new closure over the context in which you’re doing it.Off-topic: The way you’re specifying
Personwill break certain things people might expect to work, such as:…because you’re replacing the object
newcreated for you, but returning a different object out of your constructor (which overrides the default).Rather than creating a new object and returning that in your constructor, allow the object constructed for you by
newto be the object (and thus thePersonrelationship is maintained), but you can still get truly private variables and use accessors:Live example
As you can see, this is dramatically simpler, and it preserves the
instanceofrelationship. Note that we’re not qualifying our references tonamewithinNameat all, and so we’re using the local variable in the constructor call in which ourNamefunction, which closes over it, was created.I’ve also taken the liberty there of giving the constructor function a name, because I’m not a fan of anonymous functions. I should have given the accessor a name as well:
Off-topic 2: The overwhelming convention in JavaScript code is to use initial caps on function names only for constructor functions (like
Person), and not on other kinds of functions (likeName). You’re free to do whatever you like, of course, but I thought I’d mention the convention, as it makes it easier for other people to read your code.Worth noting: All of these techniques result in every single
Personobject having its own copy of the accessor function. If there are going to be a lot of these objects, that could be a memory issue. If there are only going to be a few, that’s fine.