tl;dr
Is the suggestion of preferring prototype methods to instance method a general one, or does it apply only to the cases where the method in question is indeed independent of other instance properties?
Or, in other words, if I’m wishing to use instance methods because I want such a method to be parametrised by the individual object on which it is invoked, am I misdesigning my JavaScript application, because there are better ways to achieve this (i.e. methods that depend on the state of the object, e.g. on its instance variables)?
Longer version
In ยง7.2.1 from Secrets of the JavaScript Ninja – 2nd ed., the difference is explained between defining an instance method
function Ninja() {
this.swung = false;
this.swingSword = function() {
return 'swing';
};
}
const n1 = new Ninja();
const n2 = new Ninja();
console.log(n1.swingSword())
console.log(n2.swingSword())
vs defining a prototype method
function Ninja() {
this.swung = false;
}
Ninja.prototype.swingSword = function() {
return 'swing';
}
const n1 = new Ninja();
const n2 = new Ninja();
console.log(n1.swingSword())
console.log(n2.swingSword())
Besides the fact that, if both are defined, the former takes precedence, the claim is made that the former also imposes a performance penalty (at least if the number of created objects is large enough), because a new copy of the method will be created for each new Ninja()
invocation.
And I understand that, but that claim is not meant to apply to this simple toy example, but to be of general guidance (at least that’s the way I interpret the text of the book!), and I don’t understand why things should be like that.
Indeed, I can’t help but thinking of a slightly more complex example, where the constructor has some parameters which are captured by the closures of the instance methods, which means that effectively the function will be different for every object:
function Ninja(foo) {
this.swung = foo;
this.swingSword = function() {
return foo;
};
}
const n1 = new Ninja('one');
const n2 = new Ninja('two');
console.log(n1.swingSword()) // this calls function() { return 'one'; };
console.log(n2.swingSword()) // this calls function() { return 'two'; };
In this case, I think, a prototype method is not an option, because it would be common across the object, and so it couldn’t be customized by each object, no?
And this also applies to member variables, e.g. swung
in the last example must be an instance property, but in the original example could well be a prototype property, because it’s just false
, it doesn’t change.
function Ninja() {}
Ninja.prototype.swung = false;
Ninja.prototype.swingSword = function() {
return 'swing';
}
const n1 = new Ninja();
const n2 = new Ninja();
console.log(n1.swung)
console.log(n2.swung)