类与原型Array.isArray

时间:2018-07-10 14:59:32

标签: javascript arrays class prototype

人们总是被告知“类只是原型的语法糖。”

但是在此示例中,这表明事实并非如此。

function SubArray() {}
SubArray.prototype = new Array( );
console.log(Array.isArray(new SubArray()))  // false

和带有类的相同示例。

SubArray = class extends Array{}
console.log(Array.isArray(new SubArray()))  // true

有趣的是,instanceofnew SubArray instanceof Array上都可以正常工作。 为什么Array.isArray不在这里返回true和原型?

5 个答案:

答案 0 :(得分: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

上面的行为应与您使用class语法提供的示例相同。不幸的是,如果没有其他new.targetReflect.construct()之类的ES6构造,并非所有这些行为都可以准确地重现,但是为了产生所需的行为,至少不是必需的:

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

这里的关键是将实例化对象的构造委托给Array构造函数,以便将该对象初始化为Array外来对象。因此,假设,严格所必需的全部内容如下:

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

console.log(Array.isArray(new SubArray())) // true

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

编辑

我做了一些修改,我个人认为这是一个可怕的主意,但是如果您想在ES5中尽可能模拟class,则可以选择退出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))

答案 1 :(得分:0)

请考虑执行Array.isArray()的步骤:

  

IsArray(参数)

     

抽象操作IsArray接受一个实参,然后   执行以下步骤:

If Type(argument) is not Object, return false.
If argument is an Array exotic object, return true.
If argument is a Proxy exotic object, then
    If the value of the [[ProxyHandler]] internal slot of argument is null, throw a TypeError exception.
    Let target be the value of the [[ProxyTarget]] internal slot of argument.
    Return IsArray(target).
Return false.

您的示例在以下步骤失败:“如果参数是Array奇异对象,则返回true。”因为它没有 own length属性。

function SubArray() {}
SubArray.prototype = new Array( );
console.log(Array.isArray(new SubArray())) // false


const sa = new SubArray();
console.log(typeof sa); // object
console.log(Object.getOwnPropertyDescriptor(sa, "length")); // undefined

答案 2 :(得分:0)

由于在聊天中使用rlemon,您可以执行与Patrick在Reflect中所做的相似的操作,并且理论上应该具有IE11支持。

function Foo() {
    const arr = new Array(...arguments);
    arr.__proto__ = Foo.prototype;
    return arr;
}

Foo.prototype.constructor = Foo;

Foo.prototype.__proto__ = Array.prototype;

Array.isArray( new Foo(123) ) 

答案 3 :(得分:0)

此方法利用iframe,并且如果您选择使用类扩展功能,则可以扩展该类。

var iframe = document.createElement("iframe");
iframe.style.display = "none";
document.body.appendChild(iframe);

frames[frames.length - 1].document.write(
  "<script>parent.SubArray = Array;<\/script>"
);

SubArray.prototype.__proto__ = Array.prototype;
SubArray.__proto__ = Array;
console.log(Array.isArray(new SubArray()));
console.log(new SubArray() instanceof Array);
console.log(new SubArray() instanceof SubArray);

SubArray2 = class extends SubArray {} 

console.log(new SubArray2() instanceof SubArray)

答案 4 :(得分:-2)

ES5的处事方式不允许您从15正确继承。 ES6使它正常工作。