扩展常规对象以支持ES5阵列功能的问题

时间:2013-03-12 22:17:58

标签: javascript object ecmascript-5 ecmascript-harmony ecmascript-6

我很久以前一直愿意让原生数组和常规对象之间的界限完全模糊,不仅扩展了与ES5中的数组相同的功能,而且还捆绑了我自己定制的方法包。 / p>

一些聪明人想到了这些范式的变化。就像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"

这种方法对我来说非常有趣。然而,它似乎也遭遇了几个严重的问题!

  1. 如何使用几个新属性扩展原始myObj模型? 我们是否应该对每个属性更改运行extendAsArray以更新有关length属性的内容?

  2. 当一个属性发生变化时,不仅仅是length属性是相关的; 数组索引也应该更新,因为类似数组的属性请求肯定是未定义的。所以当

    console.log(myObj.length) -> 4
    myObj.zAxis=0
    

    然后

    console.log(myObj[4]) // -> undefined!
    console.log(myObj.length) // -> 4!
    
  3. 我已相应修改了安格斯的代码,因此它支持根据请求自动更新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({}).mapEveryz.Object([]).mapEvery

    相同

    请避免提及JQuery和Underscore。我有一个全面的,自定义的两种类型的方法列表,我愿意使用可能与我未经修改的标准完成的标准,我不愿意重构它!

2 个答案:

答案 0 :(得分:1)

我想这是你的问题:

  

在更改,添加或删除属性时,如何更新对象的数组索引及其length属性?

您创建了执行此操作的方法,因此您基本上可以模仿Object internal methods。我不认为你可以用吸气剂和制定者做到这一点,但我可能错了。

其余的更多是评论而不是答案。

  

我很久以前一直愿意让原生阵列和普通物体之间的界线完全模糊

这条线已经完全模糊了。数组是对象,唯一能区分它们的是它们的特殊长度属性。

  

EcmaScript 5显然通过引入通用方法

来抢占这一趋势

ES5没有引入通用方法,至少从第3版就开始使用。

  

由一种类型定义但可由任何

使用

完全没有,事实上ES5更具限制性。在第3版中,callapply使用Object(*thisArg*) thisArg 强制转换为对象,或者如果未传递任何内容,则替换全局对象。在ES5中没有通过 thisArg 未经修改。

使用数组作为对象的限制与约定有关,而不是语言本身。大多数开发人员都认为应该使用对象或数组之间存在明显的区别。在极少数情况下,您确实需要像对象一样使用数组,但毫无疑问它们存在。 jQuery是Object利用Array属性的例子,例如:选择器收集的元素作为数字属性添加,并且有一个length属性,即元素的数量。通过这种方式,通用数组方法可以应用于jQuery对象(顺便说一下,所有这些都在ed 3中)。

Object.watch方法在JavaScrpit™中,它不是ES5的一部分,因此请谨慎使用。

创建自己的内置对象版本的一个主要问题是,您可能最终将每个内置方法包装在本地方法中(比如jQuery几乎包装每个DOM方法)并开始设置getter和setter在每个属性上,或最终用函数调用来替换属性访问(例如jQuery的valattrprop方法。如果表现很重要,那就相当乏味,而且很慢。

哦对不起,我提到了jQuery ......: - (

设计一个库或框架以充分利用该语言所具有的功能似乎更为明智,而不是试图强迫它执行它做得不好的事情,或者不做本机操作

但尝试满分。 : - )

答案 1 :(得分:0)

有一个图书馆watch.js,它正在关注房产更新或新增加的房产。 try out!

它正在使用setInterval,因此它不是性能友好的。

当和谐消失时,我们可以做一些简单的事情:

Object.observe(obj,Observer);

检查规格: Harmony

但是,当后来的对象扩展不在焦点时,我可以在初始化时冻结整个对象,不会因为属性更改和属性添加而烦恼。

代码会相应更改:

extendAsArray = function z_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', {
        value:index
       });
       if(!Object.freeze){
          Object.defineProperty(Object, "freeze", {
                enumerable: false
              , configurable: false
              , writable: false
              , value: function (obj) {
                  var props = Object.getOwnPropertyNames(obj);
                  for(var i=0; i<props.length; i++){
                      var desc = Object.getOwnPropertyDescriptor(obj,props[i]);
                      if("value" in desc ){
                         desc.writable = false;
                      }
                      desc.configurable = false;
                      Object.defineProperty( obj, props[i], desc );
                  }
                  return Object.preventExtensions(obj);
              }
          });
       }
       Object.freeze(obj);
    }
    return obj;
};

此外,我还发现了上一篇文章中提到过的安格斯·克罗尔(Angus Croll)所谈到的内容。

“是的,我们可以利用井下编写的库提供的等效功能,例如underscore.js,但我们仍然被锁定在非标准的反向签名中,其中方法是静态的,对象只是额外的参数 - 对于仅实例语言的笨拙安排。在某些时候,所有支持的浏览器都将符合ES5标准,此时,填充的代码库可以简单地删除它的填充库并继续进行,而未经修改的代码库必须在主要重构或永久非选择之间进行选择。标准和静态实用程序库。“