for-in和for-of区别
先说说结论
- for-in适合遍历对象属性,for-of适合遍历数组
- for-in循环出的是key值,for-of循环出的是value
- for-in可以遍历可枚举的属性,for-of遍历的是可迭代的
- for-of不能直接遍历普通的对象,需要通过Object.keys()搭配使用
for-in循环
最早期遍历数组的方式就是一层for循环遍历数组下标:
let a=[1,2,3,4];
for(let i=0;i<a.length;i++){
console.log(a[i]);
}
到后面ES5发布之后,又可以通过forEach来遍历数组,但是使用forEach的缺点就是不能中断
。
既然想到通过for来循环,就会想到for-in:
let a=[1,2,3,4];
a.name="zhangsan"
console.log(a)
for(let index in a){
console.log(index)
}
但是这样会出现以下几个问题:
- 赋值给index并不是一个数字,而是一个字符串
- for-in除了遍历数组中的元素之外,还回去遍历自定义的属性,甚至数组原型链上的属性都能访问到。
- for-in可会不会按照数组的顺序遍历数组。
for-of循环
for-of方法修复了for-in循环的缺点,并且和forEach不同的是他还能通过break
、return
终止循环,同时 除了数组之外,还支持像Set、Map、类数组对象等数据结构, 注意:for-of
循环不支持普通对象,但是如果你想迭代一个对象的属性,可以使用for-in
循环或者内建的Object.keys()方法
。
for-of循环原理
在说原理之前先说说迭代器,那什么是迭代器呢?
所谓迭代器就是一个拥有next()方法的对象,每次调用next()方法都会返回一个结果,该结果有两个属性,一个是value表示当前获取的值,第二个是done表示遍历是否结束,下面手动实现一个迭代器:
function createIterator(items){
function addIterator(items){
let i = 0
let done = false
return {
next() {
done = i >= items.length
return {
value: items[i++],
done
}
}
}
}
let iterator =[];
//给iterator添加一个Symbol.iterator属性,只有这样这种数据才是"可遍历的"。
iterator[Symbol.iterator] = ()=> addIterator(items)
return iterator
}
let a=[1,2,3,4];
let iter=createIterator(a)[Symbol.iterator]();
console.log(iter.next())//{ value: 1, done: false }
console.log(iter.next())//{ value: 2, done: false }
console.log(iter.next())//{ value: 3, done: false }
console.log(iter.next())//{ value: 4, done: false }
console.log(iter.next())//{ value: undefined, done: true }
这其实也就是for-of的原理,他其实帮我们做的就是获取数据的迭代器对象,然后一个一个遍历。
那这里就有一个疑问了,我们平时定义的数组没给他添加 Symbol.iterator 属性啊,其实是数组默认 部署了Symbol.iterator 属性。
下面就模拟实现下for-of:
function forOf(obj, cb) {
let iterable, result;
if (typeof obj[Symbol.iterator] !== "function")
throw new TypeError(result + " is not iterable");
if (typeof cb !== "function") throw new TypeError("cb must be callable");
iterable = obj[Symbol.iterator]();
result = iterable.next();
while (!result.done) {
cb(result.value);
result = iterable.next();
}
}