很久以前,我一直愿意完全模糊原生数组和常规对象之间的界限,不仅扩展了与 ES5 中的数组相同的功能,而且在双方都捆绑了我的自定义方法包。
几个聪明人想到了这些范式变化。就像 Angus Croll 在文章javascript-object-keys-finally中提到的那样:
“此外,随着数组和常规对象之间的界限模糊(在自定义 getter 和 setter 的帮助下),我们很可能会看到通用的“类数组”对象的增长,它们享有两全其美——非数字标识符和访问由 Array.prototype 定义的丰富的 API 集。EcmaScript 5 通过引入由一种类型定义但可供任何类型使用的泛型方法显然抢先了这一趋势。”
一路上,他得到了文章中的编码: extending-objects-with-javascript-getters
function extendAsArray(obj) {
if (obj.length === undefined || obj.__lookupGetter__('length')) {
var index = 0;
for (var prop in obj) {
if(!obj.__lookupGetter__(prop)) {
(function(thisIndex, thisProp) {
obj.__defineGetter__(thisIndex, function() {return obj[thisProp]});
})(index, prop)
index++;
}
};
obj.__defineGetter__("length", function() {return index});
}
return obj;
}
var myObj = {
left:50,
top:20,
width:10,
height:10
}
extendAsArray(myObj);
[].map.call(myObj,function(s){return s+' px'}).join(', ');
//"50px ,20px ,10px, 10px"
这种方法对我来说非常有趣。但是,它似乎也遇到了一些严重的问题!
用几个新属性扩展原始 myObj 模型怎么样?我们是否应该
extendAsArray
在每个属性更改上运行以更新它的相关length
属性?当属性发生变化时,相关的不仅仅是
length
属性;数组索引也应该更新,因为类似数组的属性请求肯定是未定义的。所以当console.log(myObj.length) -> 4 myObj.zAxis=0
然后
console.log(myObj[4]) // -> undefined! console.log(myObj.length) // -> 4!
我已经相应地修改了 Angus 的代码,因此它支持根据length
请求自动更新属性:
function extendAsArray(obj) {
var index = 0;
for(var prop in obj){
if(!obj.__lookupGetter__(prop)){
(function(thisIndex, thisProp){
Object.defineProperty(obj, thisIndex, {
get: function(){return obj[thisProp]}
, enumerable: true
, configurable: true
, writeable: true
});
})(index, prop)
index++;
}
}
if(!obj.__lookupGetter__('length')){
Object.defineProperty(obj, 'length', {
get: function(){
return extendAsArray(obj);
}
, configurable: true
, writeable: true
});
return obj;
}
else{
return index;
}
}
length
问题是:当一个属性被改变、添加或删除时,我们如何更新对象的数组索引及其属性?
我应该使用Object.watch
吗?
还有一个未解决的问题:如何干扰我自己的未填充实用程序库,并以一致的方式使其也适用于对象?
我对两种类型都使用相同的代码库:z.Object({}).mapEvery
与z.Object([]).mapEvery
请避免提及 JQuery 和下划线。我已经为这两种类型提供了一个全面的自定义方法列表,我愿意使用可能与我未填充的标准一起完成的标准,我不愿意重构它!