【问题标题】:How to make JavaScript objects and arrays behave the same way?如何使 JavaScript 对象和数组的行为方式相同?
【发布时间】:2013-07-21 08:41:04
【问题描述】:

我想编写一个迭代数组对象和对象的函数版本,并且重复代码最少。比如:

function (arr_or_obj) {
   arr_or_obj.forEach( function(key,value) {
            // do stuff with key and value
       });
}

这是在我意识到(至少在 Chrome 中)Object.keys 返回一个数组的键列表之前。所以我可以用它来把 Array 当作一个对象来对待。像这样:

function (arr_or_obj) {
   var keys = Object.keys(arr_or_obj);
   keys.forEach( function(key) {
            var value = arr_or_obj[key];
            // do stuff with key and value
       });
}

问题解决了。但这不是在我编写自己的“JavaScript 伪数组迭代器”之前。这样做的唯一好处是,我们无需获取 Array 的键列表(可能很长),而是通过仅生成我们需要的返回值来节省内存。像这样:

    function create_iterator(max) {
            var jkeys = {};
            jkeys.count_ = 0;
            jkeys.length = max;
            jkeys.getter_ = function() {
                    var returnable = jkeys.count_;
                    jkeys.count_ += 1;
                    jkeys.__defineGetter__(jkeys.count_,jkeys.getter_);
                    delete jkeys[jkeys.count_-1];
                    return returnable;
            };
            jkeys.__defineGetter__(0, jkeys.getter_);
            return jkeys;
    }

然后你可以通过 go 调用:

var z = create_iterator(100);
z[0];
>> 0
z[0];
>> undefined
z[1];
>> 1;
z[2]
>> 2;
...

这有点像一问一答,但显而易见的问题是,有没有更好的方法可以在不使用Object.keys 的情况下做到这一点?

【问题讨论】:

    标签: javascript iterator


    【解决方案1】:

    Object.keys 为您提供对象的自己的可枚举属性的数组。也就是说,它本身拥有的属性,而不是来自它的原型,并且是可枚举的(因此不包括数组上的 length 之类的东西,这是一个不可枚举的属性)。

    如果您使用正确的保护措施,您可以对 for-in 做同样的事情:

    var key;
    for (key in arr_or_obj) {
        if (arr_or_obj.hasOwnProperty(key)) {
            // do something with it
        }
    }
    

    现在您正在做与 Object.keys 相同的事情。

    但是,请注意,这(和Object.keys)将包括人们放在数组上的属性,不是数组索引(这在 JavaScript 中完全有效) .例如:

    var key;
    var a = [1, 2, 3];
    a.foo = "bar";
    for (key in a) {
        if (a.hasOwnProperty(key)) {
            console.log(key); // Will show "foo" as well as "0", "1", and "2"
        }
    }
    

    如果你只想处理数组 indexes 而不是其他属性,你需要知道这个东西是否是一个数组,如果是,那么使用“这是一个索引”测试:

    var key;
    var isArray = Array.isArray(arr_or_obj);
    // Or (see below):
    //var isArray = Object.prototype.toString.call(arr_or_obj) === "[object Array]";
    for (key in arr_or_obj) {
        if (a.hasOwnProperty(key) &&
            (!isArray || isArrayIndex(key))
           ) {
            console.log(key);   // Will only show "0", "1", and "2" for `a` above
        }
    }
    

    ...Array.isArray 是 ES5 的一个新特性,如果需要,它可以很容易地为旧版浏览器填充:

    if (!Array.isArray) {
        Array.isArray = (function() {
            var toString = Object.prototype.toString;
            function isArray(arg) {
                return toString.call(arg) === "[object Array]";
            }
            return isArray;
        })();
    }
    

    ...而isArrayIndex 是:

    function isArrayIndex(key) {
        return /^0$|^[1-9]\d*$/.test(key) && key <= 4294967294; // 2^32 - 2
    }
    

    请参阅this other answer 了解详情(幻数的来源等)。

    如果不是数组,上面的循环将遍历对象自己的可枚举属性(所有属性),如果是数组,则循环遍历索引(但不是其他属性)。

    更进一步,如果您想包含对象从其原型中获得的属性怎么办? Object.keys 省略了那些(按设计)。如果要包含它们,请不要在 for-in 循环中使用 hasOwnProperty

    【讨论】:

    • 一如既往的令人印象深刻的回应,但也许使用Array.isArray(arr_or_obj);而不是Object.prototype.toString.call(arr_or_obj) === "[object Array]";会更好
    • @Givi:好点子,我允许使用旧版浏览器和不使用 ES5 shim 的人。
    【解决方案2】:

    你可以试试这样的...
    Live Demo

    function forIn(obj) {
        var isArray = Array.isArray(obj),
            temp = isArray ? obj : Object.keys(obj);
        temp.forEach(function (value, index, array) {
            console.log(this[isArray ? index : value]);
        }, obj);
    }
    

    【讨论】:

    • 可爱!和+1。但我认为 OP 说他们希望尽可能避免构建键数组的开销。
    【解决方案3】:

    你可以写:

    for (key in arr_or_obj) {
        if (arr_or_obj.hasOwnProperty(key) {
            value = arr_or_obj[key];
            // do stuff with key and value
        }
    }
    

    【讨论】:

    • 除非你采取正确的保护措施,否则使用它来循环数组是个坏主意。更多内容请参见this answer
    • @T.J.Crowder 该答案假定您先验地知道您正在处理一个数组,因此您可以限制键值。他希望对数组和对象一视同仁。
    • @Barmar:我没有说答案回答了这个问题,只是解释了(在 Use for-in 正确部分)为什么在没有保护措施的数组上使用for-in 是个坏主意,除非您知道自己在做什么(例如,您确实想处理非索引属性)。
    猜你喜欢
    • 2016-04-30
    • 1970-01-01
    • 2016-07-12
    • 2018-08-27
    • 2020-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多