Because of this line in getHasEngine:
vehicle.calls = vehicle.calls + 1;
That's directly accessing the vehicle object. Since boat doesn't have its own calls property, it inherits it from vehicle, so boat.calls is 1.
Also, why is sailboat.calls set to 2 after only calling getHasEngine() once?
Also because of that line: It sets vehicle.calls to 1, and at that point, sailboat doesn't have its own calls, so it inherits the property. Then on the next line when you do:
this.calls = this.calls + 1;
that's reading vehicle.calls (1), adding 1 to it, and then assigning the result (2) to sailboat.calls.
Let's throw some ASCII-art at it. After creating your objects but before callling getHasEngine, you have this in memory (omitting details):
+−−−−−−−−−−+
vehicle−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+−−>| (object) |
| +−−−−−−−−−−+
| | calls: 0 |
| +−−−−−−−−−−+
+−−−−−−−−−−−−−−−+ |
boat−−−−−−−−−−−−−−−−−−−−−−−−−−−+−−>| (object) | |
| +−−−−−−−−−−−−−−−+ |
| | [[Prototype]] |−−+
| +−−−−−−−−−−−−−−−+
|
+−−−−−−−−−−−−−−−+ |
sailboat−−−>| (object) | |
+−−−−−−−−−−−−−−−+ |
| [[Prototype]] |−−+
+−−−−−−−−−−−−−−−+
Note that neither boat nor sailboat has a calls property.
After getHasEngine is called once, you have this — note that sailboat now has a calls property (because it was created by this.calls = this.calls + 1;):
+−−−−−−−−−−+
vehicle−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+−−>| (object) |
| +−−−−−−−−−−+
| | calls: 1 |
| +−−−−−−−−−−+
+−−−−−−−−−−−−−−−+ |
boat−−−−−−−−−−−−−−−−−−−−−−−−−−−+−−>| (object) | |
| +−−−−−−−−−−−−−−−+ |
| | [[Prototype]] |−−+
| +−−−−−−−−−−−−−−−+
|
+−−−−−−−−−−−−−−−+ |
sailboat−−−>| (object) | |
+−−−−−−−−−−−−−−−+ |
| [[Prototype]] |−−+
| calls: 2 |
+−−−−−−−−−−−−−−−+
Here's a version with some logging to help show that a bit:
let vehicle = {
calls: 0,
hasEngine: null,
getHasEngine: function() {
console.log("updating vehicle.calls");
vehicle.calls = vehicle.calls + 1;
console.log("vehicle.calls is now vehicle.calls");
console.log("About to update this.calls, does this have calls?", this.hasOwnProperty("calls"));
this.calls = this.calls + 1;
console.log("Just updated this.calls, does this have calls?", this.hasOwnProperty("calls"));
return this.hasEngine;
},
canFly: null,
getCanFly: function() {
vehicle.calls = vehicle.calls + 1;
this.calls = this.calls + 1;
return this.canFly;
}
}
let boat = Object.create(vehicle);
boat.canFly = false;
let sailboat = Object.create(boat);
sailboat.hasEngine = false;
let fishingBoat = Object.create(boat);
fishingBoat.hasEngine = true;
console.log(vehicle.calls); // 0
console.log(boat.calls); // 0
console.log("boat has calls?", boat.hasOwnProperty("calls"));
console.log(sailboat.calls); // 0
console.log("sailboat has calls?", sailboat.hasOwnProperty("calls"));
sailboat.getHasEngine();
console.log(vehicle.calls); // 1
console.log(boat.calls); // 1
console.log("boat has calls?", boat.hasOwnProperty("calls"));
console.log(sailboat.calls); // 2
console.log("sailboat has calls?", sailboat.hasOwnProperty("calls"));
.as-console-wrapper {
max-height: 100% !important;
}
If you remove the vehicle.calls = line, you see it stop happening:
let vehicle = {
calls: 0,
hasEngine: null,
getHasEngine: function() {
//vehicle.calls = vehicle.calls + 1;
this.calls = this.calls + 1;
return this.hasEngine;
},
canFly: null,
getCanFly: function() {
//vehicle.calls = vehicle.calls + 1;
this.calls = this.calls + 1;
return this.canFly;
}
}
let boat = Object.create(vehicle);
boat.canFly = false;
let sailboat = Object.create(boat);
sailboat.hasEngine = false;
let fishingBoat = Object.create(boat);
fishingBoat.hasEngine = true;
console.log(vehicle.calls); // 0
console.log(boat.calls); // 0
console.log(sailboat.calls); // 0
sailboat.getHasEngine();
console.log(vehicle.calls); // 0
console.log(boat.calls); // 0
console.log(sailboat.calls); // 1