你可以在javascript中使属性可枚举但不可迭代吗?

时间:2014-07-22 01:21:13

标签: javascript ecmascript-6

我只是想知道是否有办法让对象属性可以像for in循环那样枚举,但不会出现在for of loop类似

Object.defineProperty({},'prop',{
    enumerable:true,
    iterable:false
}

如果没有,有没有计划实现这样的功能?或者for of循环是否使用可枚举属性

2 个答案:

答案 0 :(得分:4)

我在Mozilla开发网络(MDN)上进行了一些挖掘。

原来,对象有一个obj.propertyIsEnumerable(prop)方法来检查属性是否可枚举。根据MDN中给出的示例,通过原型链继承的属性不可枚举,因此该方法对这些属性返回false。

非可枚举属性的一个示例是构造函数和数组的属性length

对于问题的可迭代部分,我将引用MDN:“ECMAScript 6中的Iterable是一个接口(或者换句话说,一个协议),而不是像Array或Map这样的对象类型。”

也就是说,对象需要实现此接口以使其属性可迭代。这是内置迭代的情况,例如String,Array,Map,Set,WeakSet和Generator对象。

以下代码说明了这一点:

var aString = "hello"
typeof aString[Symbol.iterator] // "function"
aString[Symbol.iterator]() + "" // "[object String Iterator]"
[...aString]                    // ["h", "e", "l", "l", "o"]

当然,您可以定义自己的迭代器实现。

回到这个问题,附加到对象或其原​​型的属性(直接,意思不是通过继承)将在for...in循环中显示为可枚举。对于iterables,您需要一个实现,或者前面提到的实现,或者您自己的实现。这是MDN的一个很好的例子。

let arr = [ 3, 5, 7 ];
arr.foo = "hello";

for (let i in arr) {
   console.log(i); // logs "0", "1", "2", "foo" these are property names or indexes
}

for (let i of arr) {
   console.log(i); // logs "3", "5", "7" these are actual values of an
                   // iterable implementation provided by the array prototype
}

let关键字在此上下文中与var定义相同(尽管它有更多含义,但它们超出了本文的范围)。

正如您所看到的,数组已经具有iterable接口的实现(来自数组原型),因此它在使用for...of循环时产生其值,而foo不显示属性(既不是值也不是属性名称)。

因此,如果您的对象没有实现iterable接口,则它不应该是可迭代的(原则上),因此它将在for...of循环中显示其属性。

答案 1 :(得分:2)

for in and for of evaluations之间的区别在于,其中一个使用obj.[[enumerate]]而另一个使用GetIterator(obj)来确定循环项目。 [[enumerate]] does(除非obj是代理)

  

返回Iterator object,其next方法遍历obj的可枚举属性的所有字符串值键。

GetIterator将(尝试)调用@@iterate method - 但是,普通对象不会实现此接口。

因此除非您明确说明,否则任何属性都不会出现在for of循环中:

function defineIterableProperty(obj, prop, desc) {
    let old = obj[Symbol.iterate];
    Object.defineProperty(obj, prop, desc);
    obj[Symbol.iterate] = function* () {
        if (old) yield* old.call(this);
        yield prop;
    };
    return obj;
}

(未经测试,但我希望你明白这一点)