Reputation: 849
It seems like I don't understand prototypes correctly once again.
If you call a method on an object, and it doesn't exist - does it not check the prototype for the method can run it?
like
Array.prototype.myArrayMagic = function () { ... }
Then on any array you can call arr.myArrayMagic() ?
That was my understanding, but I am now running into issue where calling a prototype method wont work unless I explicitly call it through .prototype
Also: I am restricted to ES5 code
The base module setup
function Wall() {}
Wall.prototype = {
shouldShowWall: function() {
return true;
},
show: function () {
if (!this.shouldShowWall()) {
return;
}
// Do other stuff here
}
}
module.exports = Wall;
The child object
function OtherWall() {
this.item = 'my_tracking_id',
this.minutes = 30
// ...
}
OtherWall.prototype = {
constructor: Object.create(Wall.prototype),
/**
* Override wall.prototype.shouldShowWall method for deciding if the wall should be shown at this time
*/
shouldShowWall: function() {
// return true or flase here
}.bind(this)
}
module.exports = OtherWall;
So following other answers and trying to understand as best as possible, this should be set up in a way where I can call
new OtherWall().show();
but it doesn't work, it only works if I call
new OtherWall().prototype.show();
Is this normal, or have I set it up wrong?
What do I need to change to get it to work with OtherWall().show();
Upvotes: 3
Views: 559
Reputation: 1074148
If you call a method on an object, and it doesn't exist - does it not check the prototype for the method can run it?
Yes, it does (like with any property), but this code is incorrect:
OtherWall.prototype = {
constructor: Object.create(Wall.prototype)
// ...
That won't make Wall.prototype
the prototype of OtherWall.prototype
. To do that, do this:
OtherWall.prototype = Object.create(Wall.prototype);
Then add properties on OtherWall.prototype
, starting with constructor
:
// But keep reading, I wouldn't do it quite like this
OtherWall.prototype.constructor = OtherWall;
...then shouldShowWall
, etc.
My answer here may also be useful, it shows creating a "subclass" using ES5 syntax (contrasting it with ES2015's class
syntax).
Another couple of notes:
1. This looks like it's probably not what you intend:
/**
* Override wall.prototype.shouldShowWall method for deciding if the wall should be shown at this time
*/
shouldShowWall: function() {
// return true or flase here
}.bind(this)
The this
in scope there isn't an instance of OtherWall
, it's whatever this
is outside that object literal. If you want to bind shouldShowWall
to the instance, you'll need to do that in the constructor.
2. I'd suggest making methods non-enumerable, like ES2015's class
syntax does, and the same for the constructor
property like JavaScript always has. When you create a property via simple assignment, the property is enumerable.
3. I think you have a typo in OtherWall
, you've used a ,
at the end of the first statement instead of a ;
. Technically, it works, because of the comma operator, but I don't think you were using the comma operator on purpose. :-)
Here's an example applying all of the above; Object.defineProperty
is an ES5 method (I'm out of practice writing ES5 code, but I think I've avoided all ES2015+ stuff here):
function assignNonEnumerable(target, source) {
Object.keys(source).forEach(function (key) {
Object.defineProperty(target, key, {
value: source[key],
configurable: true,
writable: true,
enumerable: false, // This is the default, but showing it here for emphasis
});
});
}
function Wall() {
}
assignNonEnumerable(Wall.prototype, {
shouldShowWall: function () {
return true;
},
show: function () {
console.log("show called");
if (!this.shouldShowWall()) {
return;
}
// Do other stuff here
},
});
function OtherWall() {
this.item = "my_tracking_id";
this.minutes = 30;
// If you want to bind it to the instance:
this.shouldShowWall = this.shouldShowWall.bind(this);
}
OtherWall.prototype = Object.create(Wall.prototype);
assignNonEnumerable(OtherWall.prototype, {
constructor: OtherWall,
shouldShowWall: function () {
// You can't bind the instance here, see the constructor above
console.log("shouldShowWall called");
return Math.random() < 0.5;
},
});
new OtherWall().show();
Upvotes: 5