class vs prototype Array.isArray

Lim*_*ime 3 javascript arrays prototype class

我总是被告知"类只是原型的语法糖".

但在这个例子中,这表明这不是真的.

function SubArray() {}
SubArray.prototype = new Array( );
console.log(Array.isArray(new SubArray()))  // false
Run Code Online (Sandbox Code Playgroud)

和类相同的例子.

SubArray = class extends Array{}
console.log(Array.isArray(new SubArray()))  // true
Run Code Online (Sandbox Code Playgroud)

Funnily instanceof两者都可以正常工作new SubArray instanceof Array.为什么不在这里Array.isArray返回true原型?

Pat*_*rts 6

那是因为你没有编写class实际上是语法糖的代码:

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
Run Code Online (Sandbox Code Playgroud)

以上操作应与您使用class语法提供的示例相同.不幸的是并不是所有的这种行为可以准确地重现,而其他ES6结构,如new.targetReflect.construct(),但至少那些不一定以生产所需的行为要求:

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
Run Code Online (Sandbox Code Playgroud)

这里的关键是您将实例化对象的Array构造委托给构造函数,以便将对象初始化为Array外来对象.所以假设,所有严格必要的是以下内容:

function SubArray () {
  return Array.call(this)
}

console.log(Array.isArray(new SubArray())) // true
Run Code Online (Sandbox Code Playgroud)

但是,当然,Array.prototype在这种情况下你将无法访问方法,因此class如果你必须支持ES5 ,你应该坚持语法或第二个例子.

编辑

我做了一些修修补补,我个人认为这是一个可怕的想法,但如果你想效仿class尽可能地在ES5,你可以选择退出strict mode,以便有机会获得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))
Run Code Online (Sandbox Code Playgroud)