Open helios741 opened 5 years ago
@helios741 总结不错,感谢分享~ 提几个 typo
如果我们不想让一个对象的属性...
这个句子双重否定表肯定了对象字面量(object)应该是静态
后面多了个乱码符@kaiye 很高兴您看的这么仔细,问题已经修改了。对于第三个问题,感觉是github的md的一个小bug,我原文中写的就是”对象字面量(object)应该是静态的“,结果在“静态”后面加了一个乱码,现在我通过在“静态”中间加个一个空格这样小tricky的方式暂时解决掉了。
如果iterable和iterable是一个对象的话,上面的代码可以简化为... 这里没有看太明白
@leotian 额,这虽然是个拼写错误但是挺严重的,应该是“如果iterable和iterator是一个对象的话”。(已经修改)
我再给您进一步解释一下:
因为最开始iterable
和iterator
不是一个对象,因为在[Symbol.iterator]() {return this}
本应该返回一个iterator
的,返回了本身(this),并且本身(this)上有对应的next方法,所以可以说iterable
和iterator
是一个对象。
应该是挺严重吧哈哈哈,还是不明白就是为什么上面说要使object变为可迭代的,后面简化版确是一个函数了,意思是函数也是一个object吗?还有一点疑惑就是,既然object是不可迭代的,那么为什么我能:
const oldObj = {name:"doug", age:34}; const newObj = {...oldObj}; console.log(newObj); // { name: 'doug', age: 34 }
而且能:
const mappie = new Map().set("name", "doug").set("age", 234).set("profession", "seeker of Cthulhu"); const arr1 = [...mappie]; console.log(arr1); // [ [ 'name', 'doug' ], [ 'age', 234 ], [ 'profession', 'seeker of Cthulhu' ] ]
但是却不能:
const obj = { name: "doug", age: 234, profession: "seeker of Chthulhu" }; const arr2 = [...obj]; console.log(arr2);
?
@leotian 先回答第一个问题哈,我们普通写的const o = {}
这个是对象的一种,一般我们称做为对象字面量,函数也是对象的。
剩下的几个代码块都基本是规范的问题(也是我文章写的不清楚,5555),
第一个代码块,可以看看已经实现的了提案proposal-object-rest-spread,虽然都是rest/spread
,但是针对的主体不同(注意一个是elements一个是properties)
对于第二个代码块:可以看看sec-map-iterable规范,因为map是实现了iterable
的,所以是可以通过[...]
进行消耗的。
第三个代码块也是规范的问题(同第二个代码块),因为对象字面量是没有实现的iterable
的,你要是下面这样就能被迭代了
const obj = {
};
obj[Symbol.iterator] = function* () {
yield { name: "doug" }
yield { age: 234 }
yield { profession: "seeker of Chthulhu" }
}
const arr2 = [...obj];
console.log(arr2);
所以说对象的rest/spread operator不是基于iterator实现的吧?spread翻译为展开的话,你觉得rest翻译成什么比较合适,压缩?
@leotian 对象的的解构不是消耗的iterable的,对象的rest是得到尚未被解构的可枚举属性key。
我觉得把rest就单纯的翻译为剩余就可以吧
文章首发
本文已经默认你已经知道
generator
是什么以及for...of
和数据类型map
怎么用了。前ES6的时代怎么遍历
先来一道思考题:
通过下面的变量
对于第一个问题来说,我们可以使用各种循环语句: for/while
for...of
那么对于第二个问题来说,因为
for/while
是不能遍历对象的,所以行不通,但是对象有一个专属的遍历方法for...in
我们来看一下怎么通过for...in来遍历:
你可能会想了,通过
for...in
去遍历数组会怎样呢? 我们看一下通过for...in
去遍历:哎呀,通过
for...in
不也照样能实现数组的遍历么,那为什么不归结到数组的遍历里面去呢! 这里面还有一些细节需要去了解(这也是上面的“对象有一个专属的遍历方法”为什么加粗),我们通过一段代码去解释:这是因为
for-in
是为普通对象({key: value})设计的,所以只能遍历到字符串类型的键。还有下面这个虽然不常用,但是也是不得不说的:
foo
属于arr上面的方法,被遍历出来是说的过去的。那么用
for...of
我们来看看会怎么样is not iterable,这个
iterable
是神马东西,我们接下来下面一步步的看。先从可迭代(iterable)和迭代器(iterator)说起
iterable是ES6对iteration(迭代/遍历)引入的接口。
如果一个对象被视为iterable(可迭代)那么它一定有一个
Symbol.iterator
属性,这个属性返回一个iterator(迭代器)方法,这个方法返回一个规定的对象(这个后面会说)。也就是说iterable
是iterator
的工厂,iterable
能够创建iterator
。iterator
是用于遍历数据结构中元素的指针。两者之间的关系
Axel Rauschmaye大神的图简直不能再清晰了。
数据消费者: javascript本身提供的消费语句结构,例如for...of循环和spread operator (...) 数据源: 数据消费者能够通过不同的源(Array,string)得到供数据消费者消费的值;
让数据消费者支持所有的数据源这是不可以行的,因为还可能增加新的数据消费者和数据源。因此ES6引入了
Iterable
接口数据源去实现,数据消费者去使用可迭代协议(iterable protocol)和迭代器协议(iterator protocol)
可迭代协议(iterable protocol)
可迭代协议(iterable protocol) 是允许js对象能够自定义自己的迭代行为。
简单的说只要对象有
Symbol.iterator
这个属性就是可迭代的,我们可以通过重写(一些对象实现了iterable,比如Array,string)/添加(对于没有实现iterable的对象比如object,可以添加这个属性,使之变为可迭代的)该熟悉使之变为可迭代的。当一个对象需要被迭代(for...of 或者 spread operator )的时候,他的
Symbol.iterator
函数被调用并且无参数,然后返回一个迭代器。迭代器协议(iterator protocol)
迭代器协议定义了一种标准的方式来产生一个有限或无限序列的值。
当一个对象被认为是一个迭代器的时候,它会至少实现
next()
方法,next()
方法返回两个属性value
(d迭代器返回的值)和done
(迭代时候已经结束)。还有几个可选的方法可以被实现,具体请看:sec-iterator-interface
iterable协议,iterator协议还有next之间的关系
然后谈谈ES6中的for...of说起
再文章的最开始我们已经说了再前ES6的时候,如何去遍历。 现在我们说说ES6新增的
for...of
的作用。for...in
在前面也已经说了,在ES6之前遍历object的时候用
for...in
循环,for...in
会遍历对象上所有可枚举的值(包括原型(prototype)上的属性),比如下面这样:如果我们只想遍历对象自身的属性,可以使用
hasOwnProperty
,如下:如果我们不想让一个对象的属性在
for...in
中被遍历出来,可是使用Object.defineProperty
来定义对象上的属性是否可别枚举(更多的属性请看:Object.defineProperty()),具体如下面代码:在这一小节的最后我们来说说
for...in
中的in操作符的含义:prop in obj
:我们来看一组例子:
这个操作符虽然也适用于数组,但是尽量还是不要用在数组中,因为会比较奇怪,如下代码:
主要是前两个比较奇怪对不对,因为对于数组
prop
代表的是数组的索引而为其存在的值。 按照这样的思路,正在看的读者你能思考一下in
操作符在字符串中是怎么的模式么?for...of能遍历的集合
只要是实现了
Interable
接口的数据类型都能被遍历。javascript内部实现的有:
并不是所有的iterable内容都来源于数据结构,也能通过在运行中计算出来,例如所有ES6的主要数据结构有三个方法能够返回iterable对象。
如果for...of不能遍历怎么办
那就数据结构(数据源)去实现iterable就可以了。
用通俗的话说就是,你如果要遍历一个对象的话,有一下几个步骤:
Symbol.iterator
那就去实现Symbol.iterator
函数要返回一个iterator
iterator
返回一个对象,对象中至少要包含一个next方法来获取value
和done
现在说说怎么使object变为可迭代的
上面我们已经铺垫了这么多了,我们说了javascript中object是不能被迭代了,因为没有实现
iterable
,现在让我们来实践一下让object变的可迭代。第一步: 先尝试着使用for...of遍历object
下面这样写肯定是不行的
第二步: 让object实现iterable接口
如果
iterable
和iterator
是一个对象的话,上面的代码可以简化为:现在生成器(generator)可以出场了
我们如果每次想把一个不能迭代的对象变为可迭代的对象,在实现
Symbol.iterator
的时候,每次都要写返回一个对象,对象里面有对应的next方法,next方法必须返回valua和done两个值。这样写的话每次都会很繁,好在ES6提供了generator(生成器)能生成迭代器,我们来看简化后的代码:
让object可迭代真的有意义么
知乎的这个回答是很有水平的了:为什么es6里的object不可迭代?
在stackoverflow中也有很高质量的回答:Why are Objects not Iterable in JavaScript?
在上面的回答中从技术方面说了为什么Object不能迭代(没有实现iterable),还说了以什么样的方式去遍历Object是个难题,所以把如何迭代的方式去留给了开发者。
但是还是要思考的一个问题就是:我们真有必要去迭代对象字面量么?
想一下我们要迭代对象字面量的什么呢?是
keys
还是values
亦或者是entries
,这三种方式在ES6提供的新的数据类型map里面都有呀,完全是可以代替object的。在这里不说object
和map
的区别,只是说说在ES6以后我们想把两个事物关联起来的时候,不一定要非得是用对象字面量
了,map
支持的更好一下。对于什么时候用对象字面量(object)什么时候使用map我们可以做一下总结:
对象字面量(object)应该是静 态的, 也就是说我们应该已经知道了里面有多少个,和对象的属性有什么
使用对象字面量(object)的一般场景有:
JSON.stringify
和JSON.parse
时候其他的情况用map,其他的情况包括:
也并不说是
map
就肯定比对象字面量(object)好,map
也有如下的缺点:JSON.stringify
/JSON.parse
参考