JavaScript 中for in 和 for of 的区别

140 阅读3分钟

一、两者区别

  • for in 主要用来取key值,for of 主要用来取value值

  • for in 可以遍历数组和对象,for of 可以遍历数组、字符串、Map( )、Set( )......所有内置迭代器的类型

  • 使用for in 遍历对象的属性时,原型链上所有属性都被遍历到,如果只想遍历自身属性,可以用hasOwnProperty()方法过滤

二、for in 使用

Object.prototype.say = "19";   // 修改 Object.prototype  
var person = { age: 18 };
for (var key in person) {
  if (person.hasOwnProperty(key)) {
    // hasOwnProperty 判断其是否为自身属性
    console.log(key, person[key]);
    // 这里用 person.key 得不到对象key的值,用person[key] 或者 eval("person."+key);
  }
}

//输出
//age 18

三、for of 获取 index 值

entries() 方法返回一个数组的迭代对象,该对象包含数组的键值对 (key/value)。

let list = ['a','b','c']
for (const [index, item] of list.entries()) {
 	console.log(index,item)
}

//输出
//0 'a'
//1 'b'
//2 'c'

四、for of 通过 break 跳出循环

for (const item of ['a','b','c']) {
     if (item === 'b') {
           break;
     }
    console.log(item)
}

//输出
//a

五、for of 工作原理

  • 一个数据结构只要部署了 Symbol.iterator 属性,就被视为具有 iterator(迭代器)接口,可以使用 for of

  • for of 循环首先会向被访问对象的 iterator 接口,获取一个迭代器对象,然后通过调用迭代器对象的 next() 方法来获取返回值,数组可以直接使用 for of 遍历是因为数组内置了迭代器,而Object没有内置迭代器。

  • 拥有迭代器的对象称为 iterable ,而迭代器叫做 iterator ,这是两个不同的概念。

六、对象添加迭代器的方法

  1. 给对象添加一个名称为 Symbol.iterator 的属性方法
  2. 这个方法必须返回一个迭代器对象,它的结构必须如下:
{
	next: function() {
		return {
			value: any, //每次迭代的结果
			done: boolean //迭代结束标识
		}
	}
}
var myObject = { a: 1, b: 2, c: 3 };

//1.简单写法
myObject[Symbol.iterator] = function(){
    const _this = this
    const keys = Object.keys(this)	//也可使用Object.getOwnPropertyNames(this)
    let index = 0
    return {
      next(){
            return {
              value: _this[keys[index++]],
              done: index>keys.length
            }
          }
     }
}

//2.标准写法
Object.defineProperty( myObject, Symbol.iterator, {
	enumerable: false,	//默认值为true  遍历属性时是否将该属性取出,true为可取,false为不可取
	writable: false,		//默认值为true	是否可以修改该属性值 true可修改,false不可修改
	configurable: true,	//默认值为true  当设置为false 则理解为该属性不可删除不可修改
	value: function() {
		const _this = this
    const keys = Object.keys(this)
    let index = 0
		return {
			next(){
	        	return {
		        	value: _this[keys[index++]],
		        	done: index>keys.length
	        	}
	        }
	    }
	}
});

//3.可复用的对象迭代器添加(通过原型委托)

//如果有很多对象(但不是所有对象都需要)都想要使用for…of怎么办?可以把前面介绍的为对象添加迭代器的代码封装成函数来复用,没有任何问题,但是大多数人是通过原型委托来复用的写法:

//首先创建一个基于对象原型扩展的iterable,并给它添加一个迭代器
const iterable = Object.create(Object.prototype,{
	[Symbol.iterator]:  {
        enumerable: false,
        writable: false,
        configurable: true,
        value: function() {
            const _this = this
            //也可使用: keys = Object.getOwnPropertyNames(this)
            const keys = Object.keys(this)
            let index = 0
            return {
                next(){
                    return {
                        value: _this[keys[index++]],
                        done: index>keys.length
                    }
                }
            }
		}
	}
})

//替换myObject的原型, 使myObject可迭代
//为了不丢失对象myObject原有的原型中的东西
//iterable在创建时将原型设为了Object.prototype
Object.setPrototypeOf(myObject,iterable)

//4.原型委托升级版

//如果你的myObject已经修改过原型了再调用Object.setPrototypeOf(myObject2,iterable) ,这意味着原来的原型会丢失,下面介绍解决办法:

//定义一个函数用于给obj添加迭代器
function iterable(obj){
    if(Object.prototype.toString.call(obj) !== "[object Object]"){
    	return //非对象,不处理
	}
	if(obj[Symbol.iterator]){
		return //避免重复添加
	}
	const it = Object.create(Object.getPrototypeOf(obj), {
		[Symbol.iterator]:  {
	        enumerable: false,
	        writable: false,
	        configurable: true,
	        value: function() {
	            const _this = this
	            //也可使用: keys = Object.getOwnPropertyNames(this)
	            const keys = Object.keys(this)
	            let index = 0
	            return {
	                next(){
	                    return {
	                        value: _this[keys[index++]],
	                        done: index>keys.length
	                    }
	                }
	            }
			}
		}
	})
	Object.setPrototypeOf(obj, it)
}

//使用:
var myObject = { a: 1, b: 2, c: 3 };

iterable(myObject)// 让myObject可迭代

//5.让所有对象支持for…of

//在对象的原型上直接添加迭代器
Object.prototype[Symbol.iterator] = function(){
    const _this = this
    const keys = Object.keys(this)
    let index = 0
	return {
		next(){
        	return {
	        	value: _this[keys[index++]],
	        	done: index>keys.length
        	}
        }
    }
}