• JS基础:for...in、for...of、forEach(详解)


     

     

    for in循环

    for...in 语句以任意顺序遍历一个对象的除 Symbol 以外的可枚举属性。
    for...in 循环只遍历可枚举属性。像 Array和 Object 使用内置构造函数所创建的对象都会继承自Object.prototype和String.prototype的不可枚举属性,例如 String 的 indexOf()  方法或 Object的toString()方法。循环将遍历对象本身的所有可枚举属性,以及对象从其构造函数原型中继承的属性
    语法:for (variable in object)    variable:在每次迭代时,variable会被赋值为不同的属性名。 object:非Symbol类型的可枚举属性被迭代的对象。
    最好不要用for...in去迭代一个数组

    1. var obj={a:1,b:2,c:3};
    2. for(var prop in obj){
    3. console.log(prop+" "+obj[prop]);
    4. }

     可以循环遍历数组

    1. var arr = ['nick','freddy','mike','james'];
    2. for(var index in arr){
    3. console.log(index +'. ' + arr[index]);
    4. }
    5. // 0. nick
    6. // 1. freddy
    7. // 2. mike
    8. // 3. james

    for in 与 for of 它们之间的主要区别在于它们的迭代方式

    for...in 语句以任意顺序迭代对象的可枚举属性,会遍历手动添加的其他键,甚至包括原型链上的键,只能获得对象的键名,不能直接获取键,为遍历对象而设计。
    for...of 语句遍历可迭代对象定义要迭代的数据,允许遍历获得键值,数组的遍历器接口只返回具有数字索引的属性,不同于forEach方法,它可以与break、continue和return配合使用。提供了遍历所有数据结构的统一操作接口。
     

    1. Object.prototype.objCustom = function() {};
    2. Array.prototype.arrCustom = function() {};
    3. let iterable = [3, 5, 7];
    4. iterable.foo = 'hello';

    每个对象将继承objCustom属性,并且作为Array的每个对象将继承arrCustom属性,因为将这些属性添加到Object.prototype和Array.prototype。由于继承和原型链,对象iterable继承属性objCustom和arrCustom。

    1. for (let i in iterable) {
    2. console.log(i); // 0, 1, 2, "foo", "arrCustom", "objCustom"
    3. }

    此循环仅以原始插入顺序记录iterable 对象的可枚举属性。它不记录数组元素3, 5, 7 或hello,因为这些不是枚举属性。但是它记录了数组索引以及arrCustom和objCustom。

    1. for (let i in iterable) {
    2. if (iterable.hasOwnProperty(i)) {
    3. console.log(i); // 0, 1, 2, "foo"
    4. }
    5. }

    这个循环类似于第一个,但是它使用hasOwnProperty() 来检查,如果找到的枚举属性是对象自己的(不是继承的)。如果是,该属性被记录。记录的属性是0, 1, 2和foo,因为它们是自身的属性(不是继承的)。属性arrCustom和objCustom不会被记录,因为它们是继承的。

    1. for (let i of iterable) {
    2. console.log(i); // 3, 5, 7
    3. }

    for of循环迭代并记录iterable作为可迭代对象定义的迭代值,这些是数组元素 3, 5, 7,而不是任何对象的属性。

    对于对象

    对于普通的对象,for...of结构不能直接使用,会报错,必须部署了 Iterator 接口后才能使用。但是,这样情况下,for...in循环依然可以用来遍历键名。

     

    1. let es6 = {
    2. edition: 6,
    3. committee: "TC39",
    4. standard: "ECMA-262"
    5. };
    6. for (let e in es6) {
    7. console.log(e);
    8. }
    9. // edition
    10. // committee
    11. // standard
    12. for (let e of es6) {
    13. console.log(e);
    14. }
    15. // TypeError: es6[Symbol.iterator] is not a function

    上面代码表示,对于普通的对象,for...in循环可以遍历键名,for...of循环会报错。

    一种解决方法是,使用Object.keys方法将对象的键名生成一个数组,然后遍历这个数组。

    1. for (var key of Object.keys(someObject)) {
    2. console.log(key + ': ' + someObject[key]);
    3. }

    在js中一般使用的循环有两种

    1.常规的for(var i=0;i

    2.for-in:for(var item in list)

    但是个人更喜欢使用第一种循环,而不喜欢几乎从来不使用for-in这种写法,原因如下:

    1.第一种写法能够很好的控制循环何时结束,以及对应的索引;而第二种循环无法控制

    2.第二种写法在某种情况下,可能会导致一些奇怪的bug

    针对原因2参见下面简单的案例:

     

    以上是在jsBin中编辑的代码,在第一段代码中,对js的数组Array添加了一个新的方法(prototype 属性使您有能力向对象添加属性和方法),为了在某些时候能够方便使用。此时,如果用for-in循环出数组arr的内容,会发现在本来想要得到的0,1,2,3的后面会多一个function(){alert("myfunction");},这应该不是我们想要得到的结果。

    注意:for..in循环会把某个类型的原型(prototype)中方法与属性给遍历出来,所以这可能会导致代码中出现意外的错误

    上面只是简单的遍历一个数组,可能出现的问题还不够明显,如下所示

     

    如果我们遍历的数组中存放的是业务对象,此时会发现,最后一次输出的是空,在被循环的数组中不存在这个名称为空的人员,此时出现的错误就导致某些业务无法正常使用或显示。

    对于使用for-in可能导出的bug,有两种方式避免

    1.在循环数组集合时,不使用for-in,统一使用for(var i=0;i

    2.在for-in循环中增加一个hasOwnProperty的判断;

    hasOwnProperty函数用于指示一个对象自身(不包括原型链)是否具有指定名称的属性。如果有,返回true,否则返回false

    该方法属于Object对象,由于所有的对象都"继承"了Object的对象实例,因此几乎所有的实例对象都可以使用该方法。

     for in , for of 和forEach三者都是循环时经常使用的,但是每个使用场景都是有轻微不同,接下来就进行一个对比

    for…in…的作用

    枚举对象

    1. const person = {
    2. name: 'Lydia',
    3. age: 21,
    4. };
    5. for (const item in person) {
    6. console.log(item);
    7. }

    这个输出结果是: name age

    对于这个结果可以简单理解为,对于对象object,使用for…in…循环是对对象的key值进行循环

    但是使用for…of…结果就不相同

    1. const person = {
    2. name: 'Lydia',
    3. age: 21,
    4. };
    5. for (const item of person) {
    6. console.log(item);
    7. }

    这个输出结果TypeError: person is not iterable

    这个结果可以看出for…of…不能对对象进行循环

    再看看forEach能怎么样?

    1. const person = {
    2. name: 'Lydia',
    3. age: 21,
    4. }
    5. person.forEach((i) => {
    6. console.log(i)
    7. })

    可枚举数组

    这个输出结果为 0 ‘a’ 1 ‘b’ 2 ‘c’

    这个结果看出使用for…in…是输出索引值,通过索引值能拿到数组数据

    但是使用for…of…结果就不相同

    1. const arr = ['a','b','c']
    2. for (const item of arr) {
    3. console.log(item);
    4. }

    这个输出结果为 ‘a’ ‘b’ ‘c’

    这个结果看出使用for…of…是输出数组值

    1. const arr = ['a','b','c']
    2. for (const item of arr) {
    3. console.log(item);
    4. }

    可枚举数组的原型对象

    1. Array.prototype.sayHello = function(){
    2. console.log("Hello")
    3. }
    4. Array.prototype.str = 'world';
    5. var myArray = [1,2,3];
    6. myArray.name='数组';
    7. for(let index in myArray){
    8. console.log(index);
    9. }

    这个输出结果是: 0 1 2 name sayHello str

    这个结果看出for in 不仅返回的是数组的下标,而且将数组的原型对象以及数组对象本身属性值都会返回。但是这也存在一个问题,在实际工作开发中,这些对象很可能是不需要的,全部列举出来可能会产生新的问题。

    为了解决原型对象这个问题,可以使用hasOwnProperty
     

    1. Array.prototype.sayHello = function(){
    2. console.log("Hello")
    3. }
    4. Array.prototype.str = 'world';
    5. var myArray = [1,2,3];
    6. myArray.name='数组';
    7. for(let index in myArray){
    8. if(myArray.hasOwnProperty(index)) {
    9. console.log(index);
    10. }
    11. }

    forEach的作用

    可遍历数组

    1. Array.prototype.sayHello = function(){
    2. console.log("Hello")
    3. }
    4. Array.prototype.str = 'world';
    5. var myArray = ['a','b','c'];
    6. myArray.name='数组';
    7. myArray.forEach((value,i) => {
    8. console.log(value)
    9. console.log(i)
    10. })

    无法break

    forEach有个问题就是不能中断执行

    1. var arr = [3, 5, 7];
    2. arr.forEach(function (value) {
    3. console.log(value);
    4. if (value === 5) {
    5. return false;
    6. }
    7. });

    输出的结果是 3 5 7

    从结果可以看出,return false没有执行,他会一直运行到底

    但是 for in 可以用break :

    1. var arr = [3, 5, 7];
    2. for (let index in arr) {
    3. console.log(arr[index]);
    4. if (arr[index] == 5) {
    5. break;
    6. }
    7. }

    输出的结果是 3 5

    从结果可以看出,for in 可以使用break

    for…of…的作用

    可遍历数组

    针对上面1.1.3中提及的问题,除了可用forEach,还可以使用for of进行处理

    1. Array.prototype.sayHello = function(){
    2. console.log("Hello")
    3. }
    4. Array.prototype.str = 'world';
    5. var myArray = ['a','b','c'];
    6. myArray.name='数组';
    7. for(let index of myArray) {
    8. console.log(index)
    9. }

    输出结果是 a b c

    使用for of无法输出索引值,但也不会输出数组的原型对象。

    可中断

    针对上面1.2.2中提及的问题,for of可以使用break进行解决

    1. var arr = [3, 5, 7];
    2. for (let value of arr) {
    3. console.log(value);
    4. if (value == 5) {
    5. break;
    6. }
    7. }

    可迭代字符串

    1. let str = 'hello';
    2. for (let value of str) {
    3. console.log(value);
    4. }

    输出结果是 ‘h’ ‘e’ ‘l’ ‘l’ ‘o’

    可迭代arguments类数组对象

    1. (function() {
    2. for (let argument of arguments) {
    3. console.log(argument);
    4. }
    5. })(1, 2, 3);

    输出结果是 1 2 3

    可迭代map和set

    1. let mapData = new Map([['a', 1], ['b', 2], ['c', 3]]);
    2. for (let [key, value] of mapData) {
    3. console.log(value);
    4. }
    5. let setData = new Set([['a', 1], ['b', 2], ['c', 3]]);
    6. for (let [key, value] of setData) {
    7. console.log(value);
    8. }

    for in 适用于纯对象的遍历,并且只能输出可枚举属性

    forEach适用于需要知道索引值的数组遍历,但是不能中断

    for of适用于无需知道索引值的数组遍历,因为可以中断。另外对于其他字符串,类数组,类型数组的迭代,for of也更适用

  • 相关阅读:
    谁能撑起植发企业的“第二曲线”?
    【PSDK】基于DOCKER的开发环境配置
    【QT】不使用QT6PDF模块,200行代码编写一个可编辑PDF的阅读器
    解除百度安全验证
    在vue3项目中使用el-tabs切换标签页时echarts图表显示不正确
    企业微信开发文档
    深入理解Python中的Lambda(匿名函数)
    java-php-python-ssm校园社团管理平台计算机毕业设计
    10 个你必须要知道的重要JavaScript 数组方法
    统信UOS 1060上通过Fail2Ban来Ban IP
  • 原文地址:https://blog.csdn.net/qq_51066068/article/details/126319814