如何检查JavaScript迭代器对象(迭代器协议)?
嘿,我来帮你把这个迭代器的问题掰扯清楚~
首先,你发现的这个现象特别有意思:你能给iterator加color、taste这些自定义属性,说明它确实是个对象,但为啥next()方法用Object.getOwnPropertyNames()或者Object.entries()找不到呢?
核心原因:next()在原型链上,不是对象自身的属性
JavaScript里的对象属性分两种:自身属性(你直接给对象加的,比如iterator.color = 'green')和继承自原型链的属性。
像matchAll()返回的这个迭代器,它的next()方法是存在于它的原型对象上的,不是挂载在迭代器自身的。而Object.getOwnPropertyNames()、Object.entries()这些方法,默认只查找对象自身的可枚举/不可枚举属性,不会去遍历原型链,所以自然找不到next()。
怎么查看迭代器的所有属性和方法?
给你几个实用的方法:
遍历原型链,手动获取继承的属性
你可以先拿到迭代器的原型,再去查看原型上的属性。比如在你的代码里加这段:// 获取迭代器的原型 const iteratorProto = Object.getPrototypeOf(iterator); text2 += "迭代器原型上的属性(包含next())=> " + Object.getOwnPropertyNames(iteratorProto) + "<br><br>";运行后你就能看到
next()出现在结果里了——它本来就存在于这个原型对象上。用浏览器控制台的
console.dir()查看完整结构
在你的脚本里加一行console.dir(iterator),然后打开浏览器开发者工具(F12)到控制台,你可以展开这个迭代器的结构:清晰看到它的自身属性(你加的color、taste),以及原型链上的所有方法(包括next())。这是最直观的方式。用
for...in循环遍历(包含继承的可枚举属性)for...in会遍历对象自身和原型链上的可枚举属性,不过要注意它会跳过不可枚举的属性(比如next()就是不可枚举的)。你可以配合Object.prototype.hasOwnProperty区分自身和继承的属性:text2 += "所有可枚举属性(含继承)=> "; for (let key in iterator) { if (iterator.hasOwnProperty(key)) { text2 += `自身属性:${key}: ${iterator[key]}, `; } else { text2 += `继承属性:${key}: ${iterator[key]}, `; } } text2 += "<br><br>";
再补充下迭代器的本质
迭代器是符合迭代器协议的对象,它的核心要求就是:必须有一个next()方法(不管是自身还是继承的),这个方法返回一个包含value(当前迭代值)和done(是否迭代完成)的对象。
你用matchAll()得到的RegExpStringIterator,它的原型链是这样的:你的iterator对象 → RegExpStringIterator.prototype(这里有next()) → Iterator.prototype → Object.prototype
所以你给它加的自定义属性是在最顶层的对象自身,而迭代需要的next()是从原型上继承来的——这就是为什么你之前的方法找不到它的原因。
给你修改后的代码示例(新增查看原型的部分)
<!DOCTYPE html> <html> <body> <h1>JavaScript Strings</h1> <h2>The matchAll() Method</h2> <p>ES2020 introduced the string method matchAll().</p> <p id="demo"></p> <script> let text = "I love cats. Cats are very easy to love. Cats are very popular." const iterator = text.matchAll(/Cats/gi); // 给迭代器加自定义属性 iterator.color = "green"; iterator.taste = "salty"; let text2 = "<hr>"; text2 += "Iterator object entries => "; for (let [key, value] of Object.entries(iterator)) { text2 += key + ": " + value + ", "; } text2 += "<br><br>"; // 新增:查看迭代器原型上的属性(包含next()) const iteratorProto = Object.getPrototypeOf(iterator); text2 += "Iterator prototype properties (including next()) => " + Object.getOwnPropertyNames(iteratorProto) + "<br><br>"; text2 += "Print out the iterator object directly => " + iterator + "<br><br>"; text2 += "Iterator object keys => " + Object.keys(iterator) + "<br><br>"; text2 += "Iterator object values => " + Object.values(iterator) + "<br><br>"; text2 += "Stringified JSON => " + JSON.stringify(iterator) + "<br><br>"; text2 += "Object.getOwnPropertyNames(iterator) => " + Object.getOwnPropertyNames(iterator) + "<br><br>"; // 调用next()消耗一次迭代 iterator.next(); text2 += "Array.from(iterator) => " + Array.from(iterator) + "<br>"; document.getElementById("demo").innerHTML = text2; // 控制台查看完整结构 console.dir(iterator); </script> </body> </html>
运行这个修改后的代码,你就能看到next()出现在原型属性的列表里啦~
备注:内容来源于stack exchange,提问作者Harry Fish

