阅读 433

for in 和for of 循环机制你理解透测了么

在面试题题中经常遇到面试官,问for..in 和for of 的区别,也许你只是只要它是两种遍历方式,for...in 可以遍历对象,for... of 不能遍历对象。除了这些的区别,就想不到什么东西了。今天就给大家盘点下,for...in. 和for... of 循环机制的优缺点,原理以及项目中的使用

for in 循环机制

1. for in 循环会优先迭代数字属性。

 let obj ={             name:'wkm',             age:20,             [Symbol('AA')]:100,             0:10,             1:20         }         for (const key in obj) {             console.log("key",key);         }          /*  输出结果          key 0          key 1          key name          key age         */ 复制代码

2. 无法迭代Symbol 类型的属性 (看上面循环没有输出Symbol 的属性)

3. 会遍历私有的和公有的可枚举属性

         Object.prototype.AAA = 200;         Object.prototype[10] = 300         let obj ={             name:'wkm',             age:20,             [Symbol('AA')]:100,             0:10,             1:20         }         for (const key in obj) {             console.log("key",key);         }         /*           key 0          key 1          key name          key age          key 10          key AAA         */ 复制代码

看上面结果,因为他遍历私有的和公有的可枚举属性,比如说项目中有个类,而且类的原型上有方法那么,用for in 去遍历它,原本不用它原型上的方法但是也会遍历到。浪费性能

基于上面的问题我们可以用obj.hasOwnProperty(key) 避免跌倒公有的属性

  Object.prototype.AAA = 200;         Object.prototype[10] = 300         let obj ={             name:'wkm',             age:20,             [Symbol('AA')]:100,             0:10,             1:20         }         for (const key in obj) {             if(!obj.hasOwnProperty(key)) break // 避免迭代公有属性             console.log("key",key);         }         /*           key 0          key 1          key name          key age         */ 复制代码

小结

从上面例子可以看出for in 循环的缺点 无法迭代Symbol 类型的属性, 会遍历私有的和公有的可枚举属性 尤其是会默认遍历公有的和私有的可枚举属性,对性能消耗会大。所以项目中尽量不用for in 循环,自己封装个遍历对象的方法。另外for in 除了可以遍历对象,还可遍历数组 和字符串,但是它不可以遍历 Set 和map 结构

封装循环对象的方法

  1. 会用到检测数据类型和检测是不是存对象的写法,下面先把工具函数封装一下

// 检测数据类型的方法封装 (function () {     var getProto = Object.getPrototypeOf; // 获取实列的原型对象。     var class2type = {};     var toString = class2type.toString;     var hasOwn = class2type.hasOwnProperty;     var fnToString = hasOwn.toString;     var ObjectFunctionString = fnToString.call(Object);     [         "Boolean",         "Number",         "String",         "Symbol",         "Function",         "Array",         "Date",         "RegExp",         "Object",         "Error"     ].forEach(function (name) {         class2type["[object " + name + "]"] = name.toLowerCase();     });     function toType(obj) {         if (obj == null) {             return obj + "";         }         return typeof obj === "object" || typeof obj === "function" ?             class2type[toString.call(obj)] || "object" :             typeof obj;     }     // 判断是不是存对象。     function isPlainObject(obj) {         var proto,             Ctor,             type = toType(obj);         if (!obj || type !== "object") { // 如果类型检测不是对象直接返回。-+             return false;         }         proto = getProto(obj); // 获取实列对象的原型对象。         if (!proto) {             return true;         }         Ctor = hasOwn.call(proto, "constructor") && proto.constructor;         return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString;     }     window.toType = toType;     window.isPlainObject = isPlainObject; })(); 复制代码

  1. 封装自己遍历对象的方法

 const eachObject = function eachObject(obj,callback){             if(!isPlainObject(obj)) throw new TypeError('obj must be an plan object');             // 保证是个function             if(!(typeof callback =="function")) callback = Function.prototype;             // 获取遍历对象的键名 数组,有Symbol 属性加上去              let keys = Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj)),             i=0,             len = keys.length,             key,             value,             result;             // 为啥用 for 不用forEach 因为forEach 循环不能终止             for(;i<len;i++){                 key= keys[i];                 value = obj[key];                 result = callback(key,value)                 if(result === false) break;             }             return obj         }         // 测试         let obj={a:1,b:2,c:3}         eachObject(obj,(key,value)=>{             console.log(key,value)         }) 复制代码

for of 循环机制

1.for 0f 循环的原理

for of 循环是基于Iterator(遍历器的) 只要拥有Iterator 机制的数据结构都能用for of 循环

1.1 遍历器机制

遍历器(Iterator)是一种接口机制,为各种不同的数据结构提供了统一的访问机制,任何数据结构只要部署了Iterator接口,就可以用for of 进行循环

1.2 遍历器机制的特点

  1. 拥有next 方法用于依次遍历数据结构成员

  2. 每一次遍历都返回一个对象{done:false, value:xxxx}

  3. Done:记录遍历是否完成

  4. value: 当前遍历的结果

根据上面手写个遍历器机制
 class Iterator {             constructor(assemble) {                 this.assemble = assemble;                 this.index = 0;             }             // 有个next 方法             next() {                 let { index, assemble } = this;                 if (index > assemble.length - 1) {                     // 说明遍历完成                     return {                         done: true,                         value: undefined                     }                 }                 // 遍历未来完成                 return {                     done: false,                     value: assemble[this.index++]                 }             }         }         let itor = new Iterator([10, 20, 30]);         console.log(itor.next())         console.log(itor.next())         console.log(itor.next())         console.log(itor.next()) 复制代码

通过修改原来的遍历器机制实现数据隔一个循环一次
 let arr = [10,20,30,40,50,60,70]         arr[Symbol.iterator] = function(){             let self = this;             index = -2;             return {                 next(){                     if(index>self.length-1){                         return {                             done:true,                             value:undefined                         }                     }                     return {                         done:false,                         value:self[index+=2]                     }                 }             }         }         for(let item of arr){         /*          循环过程:          1.首先获取[Symbol.iterator] 属性值函数 并将其执行拿到一个迭代器对象         2. 每一次循环都执行一次 iterator.next()-> {done,value}         3. 把value 的值给item 当done 为ture 时结束循环          */             console.log(item)         } 复制代码

2. 对象不具有遍历器机制,想让它用for of 循环,给它原型上加上遍历器

 Object.prototype[Symbol.iterator] = function () {             let obj = this,                 // 获取对象的键名,如果对象里有Symbol 属性拼接上,                  keys = Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj))             index = 0;             return {                 next() {                     if (index > keys.length - 1) {                         return {                             done: true,                             value: undefined                         }                     }                     return {                         done: false,                         value: obj[keys[index++]]                     }                 }             }         }         let obj = { a: 1, b: 2, c: 3 }         for (let value of obj) {             console.log(value)         } 复制代码

3 for of 小结

  • 拥有Symbol.iterator属性的数据结构(都可以被遍历)

  • for of 能遍历 数组,部分类数组, String Set Map

  • 对象默认不具有遍历器机制,所以不能用for of 遍历 要想用for of 遍历,必须在对象原型上加遍历器

总结

for in 和for of 都是用来遍历的,for in 可以遍历 对象 字符串, 数组,但是由于for 会迭代原型上的可遍历属性,因此他的性能比较差,所以项目中迭代对象的方法做好要自己封装

for of 循环是只要数据结构的原型上Symbol.iterator 方法,都能迭代,数据结构原型上没有Symbol.iterator 方法的在原型上加上遍历器方法就可以了,迭代。


作者:前端明明
链接:https://juejin.cn/post/7035854033424384007


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐