类与原型Array.isArray



我一直被告知"类只是原型的语法糖。">

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

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

以及类的相同示例。

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

有趣的是,instanceof在两个new SubArray instanceof Array上都能很好地工作。为什么Array.isArray不在这里返回带有原型的true

这是因为您没有编写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))

考虑执行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"步骤失败,因为它没有自己的长度属性。

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

多亏了聊天中的rlemon,你可以做一些类似于Patrick对out-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) ) 

此方法利用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)

ES5做事方式不允许您正确地从Array继承。ES6使其正常工作。

最新更新