1

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

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

然后您可以通过以下方式调用:

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

这是一种问答,但显而易见的问题是,有没有更好的方法来做到这一点而不使用Object.keys

4

3 回答 3

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

但是,请注意 this (and 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"
    }
}

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

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省略那些(按设计)。如果要包含它们,请不要hasOwnPropertyfor-in循环中使用。

于 2013-07-21T08:51:08.437 回答
1

你可以尝试这样的事情......
现场演示

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);
}
于 2013-07-21T09:16:11.207 回答
0

你可以写:

for (key in arr_or_obj) {
    if (arr_or_obj.hasOwnProperty(key) {
        value = arr_or_obj[key];
        // do stuff with key and value
    }
}
于 2013-07-21T08:44:01.067 回答