JavaScript:在数组上使用defineProperty访问器's长度
JavaScript: use defineProperty accessor on an array's .length?
我希望(主要是出于学术原因)能够使用Object.defineProperty()
在数组的length
上设置访问器,以便通知大小更改。
我知道ES6对象observe和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'
故障
正如您所看到的,configurable
就是false
,所以失败是可以理解的。然而,根据MDN,这应该是可能的。
如何让defineProperty
处理数组的length
属性?这样行吗?
根据ECMAScript 15.4.5.1,数组有自己的[[DefineOwnProperty]]
内部方法,因此configurable: false
不一定会立即破坏交易。这种方法的早期步骤是:
3.如果p是
"length"
,则a.如果Desc的
[[Value]]
字段不存在,则i.返回调用A的默认
[[DefineOwnProperty]]
内部方法(8.12.9)的结果,并将"length"
、Desc和Throw作为参数。
因此,如果属性描述符中没有value
属性,则属性设置操作将委托给默认的[[DefineOwnProperty]]
方法。ECMAScript 15.4.5.2要求length
属性具有configurable: false
,因此默认方法将失败。
如果设置value
,为了避免陷入默认方法,您也不能定义setter。尝试这样做将导致Chrome(或任何符合第8.10节的浏览器)出现错误:
TypeError: Invalid property. A property cannot both have accessors and be writable or have a value
因此,在任何符合ES5的实现中,似乎都不可能在数组length
上定义setter。
请注意,MDN的文章似乎是在谈论浏览器错误地拒绝使用defineProperty
设置value
,通常应该,但有时由于错误而不可能。
"向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');
由于阅读了更多关于这方面的内容,Kangax关于数组子类化主题的优秀文章涵盖了各种技术。一种被称为Array原型注入的技术用于在Ractive.js等流行库中对Array进行子类化。它依赖于非规范,但流行的__proto__被公开,但允许长度上的"访问器"。
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
正如文档中明确指出的:
仅显示Internet Explorer 9及更高版本,以及Firefox 23及更新版本以完全正确地实现长度属性的重新定义阵列。目前,不要依赖于重新定义一个数组,用于工作或以特定方式工作。甚至当你可以依赖它时,真的没有充分的理由这么做。
并非所有浏览器(包括Chrome)都支持它,您应该首先找到另一种方法来执行您想要执行的操作,而不必更改Array的length
属性。
一旦用configurable: false
定义了属性,就无法更改其配置。
所以在这种情况下,这是不可能的。即使是这样,它也会有性能问题,因为Array.length
被所有库到处使用,访问频率很高,而且到处都在不断变化。
在Array.length
中添加事件侦听器将对应用程序的整体性能产生巨大影响,您应该尽量避免这种情况。
检查"代理";对象:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
无需库
- 访问布局信息是否也会导致浏览器重排
- Javascript,访问一个主要对象模块模式中的每个对象
- 如何访问声音管理器2创建的声音对象
- 在Twitter上用ie9中的空白src访问iframe的contentWindow
- JavaScript Pub/Sub属性访问问题
- 从JavaScript访问struts操作中的属性
- 是否可以从父类访问子类的属性
- 如何访问fastOpt.js
- 创建具有可变长度幻灯片显示的幻灯片
- 访问JSON对象内部的数组元素
- 从模块内部访问Express装载路径
- 使用D3.js计算带有字母间距的文本长度
- 哪一个在访问数组长度方面的性能更好
- 如何访问ng重复中的ng重复外的ng模型长度
- Knockout-访问数组的长度“;绑定到“;来自模板内部
- Javascript:是否有任何后门方法来访问数组的真正隐藏/私有长度属性
- JavaScript:在数组上使用defineProperty访问器's长度
- 尝试访问动态生成的输入字段,并将其与长度一致的.json数据进行比较
- 如何访问此对象内部数组的长度
- 访问嵌套的json数组对象并确定数组长度