我可以将Array.prototype.sort应用于具有存取属性的某个类似Array的对象吗?

时间:2018-04-21 05:46:54

标签: javascript

上下文

const impl = Symbol()
class MyArrayLike {
    constructor() {
        this[impl] = [2, 1]
        Object.freeze(this)
    }
    get 0() { return this[impl][0] }
    set 0(value) { this[impl][0] = value }
    get 1() { return this[impl][1] }
    set 1(value) { this[impl][1] = value }
    get length() { return 2 }
}

const xs = new MyArrayLike()
Array.prototype.sort.call(xs) // (A)
console.log(xs[0], xs[1])

在上面的代码中,我希望显示已排序的值1 2。但是,Chrome和Firefox的结果不同。

  • 在Firefox中,它表现得如预期。好的。
  • 在Chrome中,它在(A)处引发了TypeError,因为访问者属性thisundefined

规范如下:https://www.ecma-international.org/ecma-262/8.0/#sec-array.prototype.sort 但是英语对我来说很难。

问题

Chrome在Array.prototype.sort的访问者属性中没有提供this这是一个错误吗?或者它是一种非特定的设计行为?

1 个答案:

答案 0 :(得分:1)

这不适用于Chrome的原因是Chrome和其他浏览器在排序调用期间处理this的方式。在其他浏览器中,this仍然是实例。但是,在Chrome中,this指的是MyArrayLike原型,它是定义访问器属性(getter和setter)的地方。在原型上,impl属性不存在(它在实例上),这就是结果未定义的原因。

与数组的一个区别是它们似乎是为实例本身的每个索引定义属性,而不是原型。您可以通过在数组实例上调用Object.getOwnPropertyNames()来测试它。这将返回索引数组和“length”。另一方面,如果您尝试在类的实例上调用该函数,则返回一个空数组。

如果您希望自己的代码也可以在Chrome上运行,那么可以选择在实例本身定义属性,例如下面的代码段:

var impl = Symbol()
class MyArrayLike {
    constructor() {
        this[impl] = [2, 1];
		Object.defineProperty(this, 0, {
			get: function() { return this[impl][0] },
			set: function(value) { this[impl][0] = value }
		});
		Object.defineProperty(this, 1, {
			get: function() { return this[impl][1] },
			set: function(value) { this[impl][1] = value }
		});
		Object.defineProperty(this, "length", {
			get: function() { return 2; }
		});
        Object.freeze(this)
    }
    
}

var xs = new MyArrayLike();
Array.prototype.sort.call(xs);
console.log(xs[0], xs[1]);

这是否是一个错误,可能需要从Chrome团队检查。但是,它也可能只是Chrome的实施方式的差异。