That's because you did not write the code that class
is actually syntactic sugar of:
function SubArray () {
if (!(new.target)) {
throw new TypeError("Class constructor SubArray cannot be invoked without 'new'")
}
return Reflect.construct(Array, arguments, new.target)
}
Object.setPrototypeOf(SubArray.prototype, Array.prototype)
Object.setPrototypeOf(SubArray, Array)
console.log(Array.isArray(new SubArray())) // true
The above should behave identically to the example you provided using class
syntax. Unfortunately not all of this behavior can be accurately reproduced without other ES6 constructs like new.target
and Reflect.construct()
, but at least those aren't necessarily required in order to produce your desired behavior:
function SubArray () {
if (!(this instanceof SubArray)) {
throw new TypeError("Class constructor SubArray cannot be invoked without 'new'")
}
return Array.apply(this, arguments)
}
SubArray.prototype = Object.create(Array.prototype)
// the line below is not necessary for Array.isArray()
// but accurately reproduces behavior of `class SubArray extends Array {}`
SubArray.__proto__ = Array // implementation hack if Object.setPrototypeOf() is not available
console.log(Array.isArray(new SubArray())) // true
The key here is that you delegate construction of the instantiated object to the Array
constructor in order to initialize the object as an Array exotic object. So hypothetically, all that's strictly necessary is the following:
function SubArray () {
return Array.call(this)
}
console.log(Array.isArray(new SubArray())) // true
But of course, you won't have access to Array.prototype
methods in this case, so you should stick to class
syntax or the second example if you have to support ES5.
Edit
I did some tinkering and I personally think this is a horrible idea, but if you want to emulate class
as closely as possible in ES5, you can opt out of strict mode
in order to have access to arguments.caller
:
// DON'T ACTUALLY DO THIS
// just for demonstration purposes
function SubArray () {
// this is about as close as you can get to new.target in ES5
if (!(this instanceof SubArray) && !(arguments.caller && this instanceof arguments.caller)) {
throw new TypeError("Class constructor SubArray cannot be invoked without 'new'")
}
return Array.apply(this, arguments)
}
SubArray.prototype.__proto__ = Array.prototype
SubArray.__proto__ = Array
// we want FooBar to extend SubArray sloppily
function FooBar () {
if (!(this instanceof SubArray) && !(arguments.caller && this instanceof arguments.caller)) {
throw new TypeError("Class constructor FooBar cannot be invoked without 'new'")
}
return SubArray.apply(this, arguments)
}
FooBar.prototype.__proto__ = SubArray.prototype
FooBar.__proto__ = SubArray
try {
SubArray()
} catch (e) {
console.log(e.toString())
}
console.log(new SubArray(1, 2, 3))
try {
FooBar()
} catch (e) {
console.log(e.toString())
}
console.log(new FooBar(1, 2, 3))