My understanding is that as the constructor property has not been set the instanceof checks should fail for new instances of Child class.
The constructor
property isn't used by instanceof
at all. In fact, until ES2015 (aka ES6), the constructor
property wasn't used for anything in JavaScript itself. It was defined as existing on the default objects the runtime assigns to the prototype
property on functions, but not used.
Let's look at how instanceof
works. Consider:
o instanceof Foo
As of ES2015, instanceof
will check to see if Foo
implements an internal operation called @@hasInstance
and, if so, it will use that operation to ask Foo
whether o
is an instance.
In ES5 and earlier (or if Foo
doesn't have that internal operation), instanceof
will look to see if the object Foo.prototype
points to is anywhere on o
's prototype chain. (If "prototype chain" is not a familiar term, see the * at the end of the answer and then come back.) If so, it returns true
; if not, it returns false
.
E.g., here's a conceptual implementaton of instanceof
, hand-waving away some details:
function isAnInstance(obj, func) {
// Start: ES2015+ part
const hasInstance = func[Symbol.hasInstance];
if (hasInstance) {
return hasInstance.call(func, obj);
}
// End: ES2015+ part
// Start: The OrdinaryHasInstance specification operation
for (let p = Object.getPrototypeOf(obj); p; p = Object.getPrototypeOf(p)) {
if (p === func.prototype) {
return true;
}
}
return false;
// End: The OrdinaryHasInstance specification operation
}
The functions built into JavaScript do have @@hasInstance
(they inherit it from Funtion
), but all it does is the OrdinaryHasInstance operation shown above. See the specification here, here, and here.
We can see that constructor
isn't involved from your question, and also from this simple demonstration:
var p = {};
var o = Object.create(p);
var Foo = function() {};
Foo.prototype = p;
console.log(o instanceof Foo); // true
console.log(o.hasOwnProperty("constructor")); // false
console.log(
Object.getPrototypeOf(o).constructor === Object
); // true
Note that:
o
wasn't created via Foo
.
- In fact,
Foo
didn't even exist until after o
was created.
o
doesn't have its own constructor
property.
o
's prototype's constructor
property is Object
, not Foo
.
...and yet instanceof
says "Yep, looks like it's a Foo." :-) Purely because the object Foo.prototype
points to is also on o
's prototype chain.
* "prototype chain"
You clearly know that objects in JavaScript have prototypes from which they inherit properties. Those prototypes are objects, and so they have prototypes. So you get a "chain" of prototypes.
Consider a two-level (arguably three-level) inheritance hierarchy, here in ES5:
function Base() {
}
function Derived() {
Base.call(this);
}
Derived.prototype = Object.create(Base.prototype);
Derived.prototype.constructor = Derived;
...or in ES2015:
class Base {
}
class Derived extends Base {
}
Now we use it:
var d = new Derived();
(Where you see "the d
object" and similar in the below, I do of course mean "the object d
refers to" — but that's really verbose.)
Now, the d
object's prototype is Derived.prototype
. Derived.prototype
's prototype is Base.prototype
. Base.prototype
's prototype is Object.prototype
. Object.prototype
doesn't have a prototype (its [[Prototype]]
internal slot is null
).
Those objects are the prototype chain underlying d
, and they mean that d
is instanceof Object
, instanceof Base
, and instanceof Derived
.