What's The Actual Purpose Of Prototype.constructor
Solution 1:
First of all, what is the constructor
property for in the prototype? It's going to sound dumb but...it gives you the constructor that created this object. So, exactly what you might imagine. However, perhaps a better question is why you'd want to do that? Well, it's actually hard to say. It's not very often you might want a constructor but it could have its uses. I'd come back to this.
Now, what happens if the constructor is not overwritten? A problem might arise if you try to dynamically fetch the constructor of some variable. Let's suppose you want to have something that copies objects. Here is a rather simplified example - we try to copy and we expect unary constructor functions that will always expect a string:
functioncopier(instance) {
const constructor = instance.constructor;
const copy = newconstructor("clone");
return copy;
}
This lets us take any object and produce a basic clone of it.
Now, let's simplify the code you have, avoid overwriting the constructor
property and use it with copier
:
functionPerson(name) {
this.name = name;
this.member = false;
}
functionCustomer(name) {
Person.call(this, name);
this.member = true;
}
Customer.prototype = Object.create(Person.prototype);
functioncopier(instance) {
const constructor = instance.constructor;
const copy = newconstructor("clone");
return copy;
}
//instantiate some objectsconst a = newPerson("Alice");
console.log(a);
const b = newCustomer("Bob");
console.log(b);
//later on we copy some object we getconst c = copier(b);
console.log(c);//c.member = false, even if b.member = trueconsole.log("c instanceof Person", c instanceofPerson); //trueconsole.log("c instanceof Customer", c instanceofCustomer); //falseconsole.log("b instanceof Customer", b instanceofCustomer);//trueconsole.log("c instanceof b.constructor", c instanceof b.constructor); //true
And here is where the problem arises. We copied c
from b
and yet while b
is a Customer
instance, c
is not. It's ultimately because b.constructor === Customer
is false
which leads to c
(the copy of b
) being incorrect and running through the wrong construction logic.
This was a rather simplified example to show how you might have a problem. Whether or not you'd have that depends on your usage of the constructor
property. In a lot of cases, you maybe don't care. However, you could also design your systems to be highly dynamic and clone generic objects. It's hard to give a generic sort of example when that would be useful but it might be. If you want to produce an easy clone functionality:
MyObject.prototype.functionclone() {
returnthis.constructor();
}
Then you'd need the constructor to be set correctly, or cloning won't work.
A slightly realistic example
Here is an actual more realistic example. It's still going to be simplified for the sake of illustrating the constructor
property but hopefully it doesn't seem too contrived.
Here is the scenario - we want to create a shooter came game. It's viewed from the top and movement is on a grid you can go up, down, left, right, and the between directions, 8 in total. We want to integrate a some splitting where some of the game objects can turn into several. It's like cloning but we produce mroe than just one extra:
- a two way split, where the object essentially does a Y split, producing two copies of itself (original ceases to exist). So if the projectile is moving "up", it produces a
\/
pattern after the split. - a three way split that is similar to the above but produces three copies of itself. if moving up it produces
\|/
pattern. - an eight-way split going in all directions.
Quick overview of how the setup would look like:
/**
* @param x - left/right position on the game map
* @param y - up/down position on the game map
* @param direction - where the object is moving. For simplicity,
* let's assume degrees but only for 8 cardinal directions: 0, 45, 90, 135, 180, 225, 270, 315, 360
also assume the underflows and overflows are normalised
* @param velocity - speed of movement of object.
*/
function GameObject(x, y, direction, velocity) {
this.x = x;
this.y = y;
this.direction = direction;
this.velocity = velocity;
}
//our main types of actors in the game. Assume each has extra behaviour.
function Player() { GameObject.apply(this, arguments) }
function Enemy() { GameObject.apply(this, arguments) }
function Projectile() { GameObject.apply(this, arguments) }
//the splitting mechanics we define//remnants go in two directions 90 degrees from one another
function split2() {
return [
new this.constructor(this.x, this.y, this.direction - 45, this.velocity),
new this.constructor(this.x, this.y, this.direction + 45, this.velocity)
]
}
//remnants go in three directions 45 degrees from one another
function split3() {
return [
new this.constructor(this.x, this.y, this.direction - 45, this.velocity),
new this.constructor(this.x, this.y, this.direction, this.velocity),
new this.constructor(this.x, this.y, this.direction + 45, this.velocity)
]
}
//remnants go in all directions, 45 degrees from one another
function split8() {
return [
new this.constructor(this.x, this.y, 0, this.velocity),
new this.constructor(this.x, this.y, 45, this.velocity),
new this.constructor(this.x, this.y, 90, this.velocity),
new this.constructor(this.x, this.y, 135, this.velocity),
new this.constructor(this.x, this.y, 180, this.velocity),
new this.constructor(this.x, this.y, 225, this.velocity),
new this.constructor(this.x, this.y, 270, this.velocity),
new this.constructor(this.x, this.y, 315, this.velocity),
]
}
Now, let's add some different enemies and weapon projectiles. Some enemies might be able to split and produce more enemies, as would some weapon projectiles. I'll take some loose inspiration from a game called Crimsonland for this example.
functionSpider() { Enemy.apply(this, arguments) }
functionZombie() { Enemy.apply(this, arguments) }
functionSplitterProjectile { Projectile.apply(this, arguments) }
functionPlasmaCannonProjectile { Projectile.apply(this, arguments) }
functionMultiPlasmaProjectile { Projectile.apply(this, arguments) }
And apply splitting behaviour to them. Most of it is not infinite but let's assume the limit of the splitting is handled elsewhere so we can just focus on the example:
Spiders spawn 8 more spiders after they die. This happens only once per spider, children don't split.
Zombies spawn two zombies on death. Also once.
the Splitter gun shoots projectiles that can do a Y split upon hitting a target. Each child can also split. Up to 8 generations of child projectiles can continue splitting, generation 9 stops.
the Plasma Cannon shoots one projectile that splits into many upon hitting an enemy. This happens once.
the Multi-Plasma gun actually shoots three projectiles at once, it doesn't split on impact. However, we can easily model the behaviour by calling split on shooting.
Spider.prototype.split = split8;Zombie.prototype.split = split2;SplitterProjectile.prototype.split = split2;PlasmaCannonProjectile.prototype.split = split8;MultiPlasmaProjectile.prototype.split = split3;
And we have the skeleton code for out entities in the game. If the constructors are wrong, now nothing would work - we wouldn't get a spider produce 8 other spiders but unknown generic entities without specific Spider behaviour to them. Same with Zombies. And projectiles will produce non-specific objects instead of more projectiles. Overall, nothing would work.
Post a Comment for "What's The Actual Purpose Of Prototype.constructor"