I'm trying to create some kind of inheritance between objects:
var foo = (function(){
function doFooStuff(){
console.log(arguments.callee.name);
}
return {
doFooStuff: doFooStuff
}
})();
var bar = (function(){
$.extend(this, foo);
function doBarStuff(){
console.log(arguments.callee.name);
doFooStuff();
}
return {
doBarStuff: doBarStuff,
}
})();
bar.doBarStuff();
bar.doFooStuff(); //<-- Uncaught TypeError:
//Object #<Object> has no method 'doFooStuff'
http://jsfiddle.net/wRXTv/
Why isn't doFooStuff
accessible here? Would you recommend another approach than using $.extend
?
$.extend(this, foo);
this
is not the object which you return from the function below (in fact it cannot be since it's created after this call), but the global object - check MDN's introduction to the this
keyword.
For what you want to do, there are two ways:
Copy all properties from foo
onto your bar
object after it is created:
var bar = (function() {
…
return {…};
})();
$.extend(bar, foo);
You can do that as well directly on the returned object:
return $.extend({…}, foo);
A variant of this pattern allows you to overwrite foo
properties. Copy foo
into an empty object, then write your bar
properties to it:
return $.extend({}, foo, {…});
Use prototypical inheritance. Create an object that inherits its properties from foo
, and then write your bar
properties to it:
return $.extend(Object.create(foo), {…});
Now when foo
changes afterward, you still can access those new properties on bar
(unless they're shadowed by own properties). Notice that Object.create
might not be supported in legacy environments, but you can easily shim it.
As noted by @raina77ow, your doBarStuff
function is flawed too. The doFooStuff
function is not in the scope of your function, and you cannot change that. You never will be able to access the private declared functions and variables from the foo
module, only those that are public - if you did need them, consider a different pattern or app design. However, doFooStuff
is a property on the exported (public) foo
object, from which bar inherits (regardless in which of the above demonstrated ways). Therefore, you can access it as a method of bar
as well, usually by using this.doFooStuff()
.
You attempt to work with revealing module as if it were a constructor. Hence an attempt to extend this
, wrong for many reasons. The most glaring, I suppose, is that neither the function is used as a constructor (no new
) nor its context is changed. In plain words, this
just points to a global object here.
But that's not the only problem. Consider that part of your code:
function doBarStuff(){
console.log(arguments.callee.name);
doFooStuff();
}
Here doFooStuff
won't be in the scope even if you somehow manage to extend this
. ) Remember, the scope resolution doesn't involve the context object.
So what's the solution? Well, I often use aggregation in similar cases:
var foo = (function(){
function doFooStuff(){
console.log(arguments.callee.name);
}
return {
doFooStuff: doFooStuff
}
})();
var bar = (function(){
var _super = foo;
function doBarStuff(){
console.log(arguments.callee.name);
_super.doFooStuff();
}
// static parent: further changes on `foo` won't affect `bar`,
// as $.extend takes the parent's current state
return $.extend({}, _super, {
doBarStuff: doBarStuff,
});
// dynamic parent: further changes on `foo` will affect `bar`,
// as extended object now has `foo` in its prototype chain
return $.extend(Object.create(_super), {
doBarStuff: doBarStuff,
});
})();
JS Fiddle.
Yes, it's aggregation, not inheritance. But so what? I'm still able to get the main prize - removing code duplication AND I'm able to control the usage of parent functions within the child module.
The problem with your code is that this in $.extend(this,foo) refers to the window object and not foo. Anonymous function are run in window's context.
I would recommend using a John Resig implementation of classical inheritance in javascript. http://ejohn.org/blog/simple-javascript-inheritance/.
Extract:
var Person = Class.extend({
init: function(isDancing){
this.dancing = isDancing;
},
dance: function(){
return this.dancing;
}
});
var Ninja = Person.extend({
init: function(){
this._super( false );
},
dance: function(){
// Call the inherited version of dance()
return this._super();
},
swingSword: function(){
return true;
}
});
var p = new Person(true);
p.dance(); // => true
var n = new Ninja();
n.dance(); // => false
n.swingSword(); // => true
// Should all be true
p instanceof Person && p instanceof Class &&
n instanceof Ninja && n instanceof Person && n instanceof Class
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With