JavaScript:在数组的.length上使用defineProperty访问器?

时间:2013-09-05 14:31:13

标签: javascript arrays google-chrome ecmascript-5 defineproperty

我希望(主要是出于学术原因)能够使用Object.defineProperty()在数组的length上设置访问者,因此我可以通知大小更改。

我知道ES6对象观察和watch.js,但我想尝试在没有额外库的情况下在ES5中执行此操作,即使这仅适用于V8 / Chrome。

示例数组:

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

Alas 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'

失败

如您所见, configurable false - 因此失败是可以理解的。但是according to MDN it should be possible

如何让defineProperty处理数组的length属性?这有用吗?

5 个答案:

答案 0 :(得分:1)

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

  

3。如果P为"length",那么

     

a。如果缺少Desc的[[Value]]字段,那么

     我是返回在传递[[DefineOwnProperty]],Desc和Throw作为参数的A上调用默认"length"内部方法(8.12.9)的结果。

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

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

  

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

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

请注意,MDN文章似乎在谈论浏览器错误地拒绝使用value来设置defineProperty通常应该,但有时候不是一个错误。

答案 1 :(得分: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');

答案 2 :(得分:1)

由于对此有了更多了解,Kangax's excellent article dedicated to the topic of subclassing Array涵盖了各种技术。一种名为数组原型注入的技术用于在流行的库(如Ractive.js)中对Array进行子类化。它依赖于非规范但流行的 __ proto __ ,但确实允许“访问者”长度。

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

答案 4 :(得分:0)

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

  

仅显示Internet Explorer 9及更高版本以及Firefox 23及更高版本   完全正确地实现长度属性的重新定义   数组。目前,不要依赖于重新定义的长度属性   一个数组,可以工作,也可以以特定的方式工作。乃至   当你可以依赖它时,没有充分的理由这样做。

包括Chrome在内的所有浏览器都不支持此功能,您应该找到另一种方法,在不更改数组的length属性的情况下,首先执行您想要执行的操作。

一旦使用configurable: false定义属性,实际上无法更改其配置。

所以在这种情况下,它是不可能的。即使它是,它也会出现性能问题,因为Array.length被所有库随处使用,经常被访问,并且随处可见。

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