Reputation: 943
Here is JavaScript code (or JSFiddle). I want to force each Test's instance to use it's own reference of self
variable. How can I achieve this?
(function (w) {
var self;
w.Test = function (name) {
// how to redeclare `self` here to not use it by reference? This doesn't help:
// self = undefined;
// delete self;
self = this;
self.name = name;
};
w.Test.prototype.getName = function () {
return self.name;
}
w.Test.prototype.test = function () {
console.debug(self.name);
console.debug(self == this);
}
})(window);
var a = new Test("a");
var b = new Test("b");
console.log(a.getName() + b.getName());
// expected: ab
// actual: bb
a.test();
b.test();
// expected: a > true > b > true
// actual: b > false > b > true
Second call overrides self
variable. How can I get expected result? I understand that the common solution is to use local self
method inside each Test's method, but is there any other way to do this without such duplication? Is there any way to redeclare closure variable?
I want to use self
instead of this
because most of my methods has some async function calls with callbacks with it's own context and in each of that methods I need separate self
variable.
Upvotes: 0
Views: 850
Reputation: 113866
In javascript, a closure is a generalisation of the concept of the global variable - it is a variable that is visible in multiple scopes. The global variable in fact can be seen as a closure itself - in global scope. There is no contradiction or confusion to call global variables closures (while some engines may implement them differently they may in fact be implemented using the exact same mechanism).
To illustrate:
var a;
var b;
(function () {
var shared; // a shared variable behaving like a sub-global variable
a = function (x) {shared = x}
b = function () {return shared}
})()
function c {return shared};
a(100);
b(); // returns 100
c(); // errors out because it is not part of the shared scope.
Functions create instances of scopes, it is unfortunate that the technical name of the instances are called closures because the word closure also refers to the underlying algorithm for creating such a thing and informally to the variable captured by the closure (the technical name for it is enclosed variable but people often just say "closure"). OOP on the other hand have completely different words for each concept - classes, instances, instantiation, properties and methods.
Since functions create instances of scope (closure) you can have more than one instance of the scope by calling the function more than once:
function makeShared () {
var shared // this thing will have more than one copy/instance in RAM
return {
a: function (x) {shared = x},
b: function () {return shared}
}
}
var x = makeShared();
var y = makeShared();
x.a(100);
y.a(200);
x.b(); // returns 100
y.b(); // returns 200
As you can see, in theory closures and objects are conceptually similar. In fact there is a paper out there that declares that they are exactly the same thing. With almost zero OOP we have created an object system (almost only because we return an object literal but if js is like Perl or PHP with real maps/hashes/associative arrays we can do it with zero OOP).
So how can we get what you want? Well, we can use a design pattern in js called the module pattern (not to be confused with js modules). It is in fact the makeShared
code I illustrate above - we abandon OOP features in js in favor of inventing our own OOP system using functional programming.
Your code would look something like this in the module pattern:
function newTest (name) { // in case you like the word "new"
var self = {};
self.name = name;
self.getName = function () {
return self.name;
}
self.test = function () {
console.debug(self.name);
console.debug(self == this);
}
return self;
};
var a = newTest("a"); // note: newTest instead of new Test
var b = newTest("b");
console.log(a.getName() + b.getName());
In the days of the revival of javascript when people started taking it seriously as a programming language (roughly 10 years after it was first created) the module pattern became a favourite amongst fans of the language. Unlike the prototype stuff it kind of looks like a class and the whole definition is enclosed in a pair of braces {}
. You can even roll your own inheritance system (with methods ranging from parasitic inheritance to prototype cloning). But this breaks the functionality of instanceof
. For most people this was a good trade-off because most consider code that need to be aware of it's own types a kind of code smell. But libraries often need to do such a thing.
We can combine the module pattern with the constructor to have almost the best of both worlds. We can do this instead:
function Test (name) {
var self = this;
self.name = name;
self.getName = function () {
return self.name;
}
self.test = function () {
console.debug(self.name);
console.debug(self == this);
}
// constructors don't need to return anything
};
var a = new Test("a"); // note: new
var b = new Test("b");
console.log(a.getName() + b.getName());
The main objection to this style of programming is that you now have multiple instances of getName
and test
so it is not as memory efficient as using the prototype stuff (this also applies to the module pattern). But some people would consider it a good trade-off.
You don't actually need to alias this
at all. All you need to do is to understand how it works and live with it (read my answer to this other question: How does the "this" keyword in Javascript act within an object literal?, I promise to keep the answer updated as long as I'm alive).
Remove all the self
stuff and just use this
. For async callbacks you just need to know how to properly call your methods:
var a = new Test("a");
// either:
someAsyncThing(function() {
a.getName();
});
// or:
anotherAsyncThing(a.getName.bind(a));
// with promises:
promiseSomething.then(a.getName.bind(a));
Upvotes: 4