6

我希望(主要是出于学术原因)能够在数组的lengthusing上设置访问器Object.defineProperty(),这样我就可以通知大小更改。

我知道 ES6 对象观察和 watch.js,但如果可能的话,我想尝试在 ES5 中执行此操作而无需额外的库,即使这仅适用于 V8/Chrome。

一个示例数组:

var demoArray = ['one', 'two']

唉 Chrome,开箱即用,使长度不可配置:

Object.getOwnPropertyDescriptor(demoArray, 'length')
Object {value: 2, writable: true, enumerable: false, configurable: false}

它不起作用:

Object.defineProperty(demoArray, 'length', { set: function(){ console.log('length changed!')} })

失败'TypeError: Cannot redefine property: length'

如您所见,configurableis false- 所以失败是可以理解的。但是根据 MDN 应该是可能的

如何defineProperty处理数组的length属性?这应该工作吗?

4

5 回答 5

2

根据ECMAScript 15.4.5.1,数组有自己的[[DefineOwnProperty]]内部方法,因此configurable: false不一定会立即破坏交易。这种方法的早期步骤说:

3. 如果 P 是"length",那么

  一个。如果[[Value]]Desc 字段不存在,则

     一世。[[DefineOwnProperty]]返回对 A 传递"length"、Desc 和 Throw 作为参数调用默认内部方法 (8.12.9) 的结果。

value因此,如果您的属性描述符中没有属性,则将属性设置操作委托给默认[[DefineOwnProperty]]方法。ECMAScript 15.4.5.2要求length属性 have configurable: false,所以默认方法会失败。

如果你确实set value,为了避免陷入默认方法,你不能也定义一个 setter。尝试这样做会导致 Chrome(或任何符合第 8.10 节的浏览器)出错:

TypeError: Invalid property. A property cannot both have accessors and be writable or have a value

因此,似乎不可能length在任何符合 ES5 的实现中在数组上定义 setter。

请注意,MDN 文章似乎在谈论浏览器错误地拒绝设置valueusing defineProperty,这通常应该是可能的,但有时由于错误而并非如此。

于 2013-09-05T15:10:31.753 回答
1

由于阅读了更多关于此的内容,Kangax 的优秀文章致力于子类化 Array 的主题,涵盖了多种技术。一种称为Array 原型注入的技术用于在 Ractive.js 等流行库中对 Array 进行子类化。它依赖于暴露的非规范但流行的__ proto __ ,但确实允许“访问器”的长度。

于 2013-10-22T13:22:24.897 回答
1

“向 Array.length 添加事件侦听器将对应用程序的整体性能产生巨大影响,您应该尽一切努力避免它”@Juno;

“不要依赖重新定义数组的长度属性来工作或以特定方式工作” MDN;

由于我们无法触及长度,因此为了获得类似的行为,我们可以更改 push 方法,并使用它向数组添加新值。

var a = ['a','b'];
a.push = function(x){
  console.log('added: ',x,', length changed: ',(this.length+1)); 
  this[this.length]=x
}
a.push('c');
于 2013-09-05T18:52:46.303 回答
1

rmprop.js让你代理一个数组,这样你就可以对.length属性做任何你想做的事情:

var rmprop = require('rmprop');
var arr = rmprop(['my','array']);

// instead, length will return sum of lengths of all elements
Object.defineProperty(arr, 'length', {
    get: function() {
        return this[unprop.real].join('').length;
    },
};

arr.length; // 7
于 2015-11-02T23:46:19.783 回答
0

正如文件上明确指出的那样:

只有 Internet Explorer 9 和更高版本,以及 Firefox 23 和更高版本,似乎完全正确地实现了数组长度属性的重新定义。目前,不要依赖重新定义数组的长度属性来工作或以特定方式工作。即使您可以依赖它,也没有充分的理由这样做。

并非所有浏览器(包括 Chrome)都支持它,您应该首先找到另一种方法来做您想做的事情,而无需更改lengthArray 的属性。

一旦用 定义了一个属性configurable: false,就真的没有办法改变它的配置了。

所以在这种情况下,这是不可能的。即使是这样,它也会有性能问题,因为Array.length所有库都在使用它,访问如此频繁,并且到处都在不断变化。

添加事件监听器Array.length将对应用程序的整体性能产生巨大影响,您应该尽一切努力避免它。

于 2013-09-05T15:14:31.037 回答