如何使JavaScript对象和数组的行为方式相同?

时间:2013-07-21 08:41:05

标签: javascript iterator

我想编写一个函数的一个版本,它迭代了Array对象和Objects,只需要很少的重复代码。类似的东西:

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

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

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伪数组迭代器”之前。这样做的唯一好处是,不是获取数组的键列表(可能很长),而是通过仅生成我们需要的返回值来节省内存。像这样:

    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;
    }

然后您可以通过以下方式致电:

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

这是一个问题和答案,但显而易见的问题是,如果不使用Object.keys有更好的方法吗?

3 个答案:

答案 0 :(得分: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的)。 E.g:

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"
    }
}

如果您只想处理数组索引而不是其他属性,则需要知道该东西是否为数组,然后如果是,请使用“这是一个索引”测试:

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省略了(按设计)。如果您想要包含它们,请不要在hasOwnProperty循环中使用for-in

答案 1 :(得分:1)

你可以尝试这样的事情......
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);
}

答案 2 :(得分:0)

你可以写:

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