了解JavaScript中的循环缺点和迭代协议

目录

介绍

背景

不同的循环构成弊端

For-loop

Array.forEach

For-in loop

For-of loop

迭代协议

迭代协议的类型

什么是迭代器协议?

什么是可迭代协议?

迭代器内置类型

检查对象/类型是否实现Symbol.iterator

内置迭代器的外观如何?

迭代器内置类型的快速示例

数组迭代器

字符串迭代器

Set迭代器

总结


介绍

如果您已经使用JavaScript或任何语言进行编程已有一段时间了,那么for-loops不应与您无关。您是否没有注意到许多编程语言(包括JavaScript)已从使用for-loops 迭代数据转变为使用返回给定集合的下一项的迭代器对象。此外,迭代器在处理集合时使我们的生活更轻松、更高效。这就是为什么理解JavaScript迭代器对每个开发人员都有益的原因,并将在本文中探讨这一主题。好,那就开始吧。

背景

在开始之前,建议您阅读JavaScript Symbols,您可以在这里查看我的文章:JavaScript Symbols基础。原因之一是我们可能会遇到Symbol.iterator的用法。请注意,JavaScript迭代器不会替换for-loops/或其他循环结构。但是,迭代器的主要目标是消除循环的复杂性和容易出错的特性。

不同的循环构成弊端

For-loop

For-loops是学习任何编程语言(包括JavaScript)的典型方法之一。通常,使用数组时,通常使用for-loop遍历数据元素。

让我们看下面的例子:

/*simple loop*/
let dogsName = ['Tank', 'Diesel', 'Cooper', 'Bruno', 'Thor'];

for (let index = 0; index < dogsName.length; index++) {
  console.log(dogsName[index]);
}
/*end of simple loop*/

我知道,代码示例非常简单。如何创建一个复杂的?

/*complex loop*/
let x = 100;

for (let i = 0; i <= x; i++) {
  for (let j = 1; (j + x / 2) < x; j++) {
    for (let k = 1; k <= x; k++) {
      console.log("hello");
    }
  }
}
/*end of complex loop*/

当嵌套循环时,复杂性会增加,这可能导致跟踪循环内的多个变量。因此,这会使您的循环易于出错。

Array.forEach

但是,如果只想遍历数据项,为什么要使用for-loop呢?Array.forEach的救援,不完全是。为什么?在回答之前,让我们先来看一个例子。

/*forEach loop*/
let amounts = [1.25, 2.25, 3.25, 4.25];

/**
 * Outputs: 
 * 1.25
 * 2.25
 * 3.25
 * 4.25
 */
amounts.forEach((item) => {
    console.log(item);
});
/*end of forEach loop*/

现在,回到问题所在。因为使用Array.forEach循环并不能让您在循环结构中中断。

让我们看下面的例子:

/*forEach loop*/
let amounts = [1.25, 2.25, 3.25, 4.25];
amounts.forEach((item) => {

  if (item === 3.25) {
    break; //throws an error: "Uncaught SyntaxError: Illegal break statement"
  }
}); 
/*end of forEach loop*/ 

For-in loop

如果仅对获取对象的那些属性感兴趣,该怎么办?forin循环就派上用场了。

让我们看下面的例子。

/** Start of for-in loop */
let customer = { firstName: "Jin Vincent", lastName: "Necesario", country: "Philippines" };

/**
 * Output:
 * firstName
 * lastName
 * country
 */
for (let key in customer) {
    console.log(key);
}

/** End of for-in loop */

但是,forin循环只能遍历可枚举的非符号属性。因此,遍历索引顺序重要的Array没有任何意义。

让我们看下面的例子。

/** Start of for-in loop that iterates over an Array */
/** Warning: Please don't do this on your project/product! */

Array.prototype.bar = 1;

let products = [1001, "mouse", "monitor", { firstName: "Jin Vincent" }];

/**Output:
 * 0
 * 1
 * 2
 * 3
 * bar
 */

for (let prop in products) {
    console.log(prop); //this outputs the index of the array and bar
}
/** End of for-in loop that iterates over an Array*/

For-of loop

如果你想遍历数组或一个集合,你不介意的索引,forof循环节省时间。

您可以将forof循环与实现可迭代协议的对象或集合一起使用。

本文后半部分将详细介绍可迭代协议。

让我们看下面的例子。

/** Start of for of loop */
const xmenGoldTeam = ['Storm', 'Bishop', 'Colossus', 'Jean Grey', 'Iceman', 'Archangel'];

/**Outputs: 
 * Storm
 * Bishop
 * Colossus
 * Jean Grey
 * Iceman
 * Archangel
 */
for (let xmen of xmenGoldTeam) {
    console.log(xmen);
}
/** end of for of loop */

因此,当对象不可迭代时,就不能使用forof循环来迭代属性。

让我们看下面的例子:

/** Start of for-of loop */
let customers = { firstName: "Jin Vincent", lastName: "Necesario", country: "Philippines" };

for (const customer of customers) {
    //TypeError: customers is not iterable
}
/** End of for-of loop */ 

迭代协议

基本上,迭代协议是对象在实现接口时需要遵循的一组规则。换句话说,当使用某种协议(它可以是迭代的或迭代器)时,您的对象应实现以下功能并使用某种规则。

迭代协议的类型

  • 迭代器协议
  • 迭代协议

什么是迭代器协议?

实现迭代器协议时,应遵循以下约定:

  • 一个对象应该实现.next()方法。
  • .next()方法必须返回一个包含以下属性的对象:valuedone

简而言之,当对象提供一种next()方法时,它是一个迭代器,该方法返回具有两个属性的一组项目序列中的下一个项目。

这两个属性完成并具有价值。

  • done——一个布尔类型,指示是否有任何现有元素可以迭代
  • value——当前元素

让我们看下面的示例并遵循约定。

/*Start of an iterator protocol*/

const countries = {
    collection: ["Philippines", "Singapore", "Malaysia", "Canada", "Brazil", "Australia"],
    index: 0,
    next: function () {
        if (this.index < this.collection.length) {
            return {
                value: this.collection[this.index++],
                done: false
            }
        } else {
            return {
                done:true
            }
        }
    }
}

console.log(countries.next()); //output: {value: "Philippines", done: false}
console.log(countries.next()); //output: {value: "Singapore", done: false}
console.log(countries.next()); //output: {value: "Malaysia", done: false}
console.log(countries.next()); //output: {value: "Canada", done: false}
console.log(countries.next()); //output: {value: "Brazil", done: false}
console.log(countries.next()); //output: {value: "Australia", done: false}
console.log(countries.next()); //output: {done: true}

/*End of an iterator protocol*/

什么是可迭代协议?

在开始之前,有一点要指出,JavaScript确实具有众所周知的符号,通常使用@@符号作为前缀。与Symbol.iterator一样,该符号也称为@@iterator

现在回到可迭代协议。当对象实现时,该@@iterator方法被认为是可迭代的。另外,此方法返回迭代器。对象的属性@@iterator key,其可通过Symbol.iterator访问。

让我们看一个例子来理解上面的陈述。

/*Start of an iterable protocol*/

const countries = {
    countryCollection: 
    ["Philippines", "Singapore", "Malaysia", "Canada", "Brazil", "Australia"],
    startIndex: 0,
    [Symbol.iterator]: function () {
        return {
            collection: this.countryCollection,
            index: this.startIndex,
            next: function () {
                if (this.index < this.collection.length) {
                    return {
                        value: this.collection[this.index++],
                        done: false
                    }
                } else {
                    return {
                        done: true
                    }
                }
            }
        }
    }
}

let countryIterator = countries[Symbol.iterator]();

console.log(countryIterator.next()); //output: {value: "Philippines", done: false}
console.log(countryIterator.next()); //output: {value: "Singapore", done: false}
console.log(countryIterator.next()); //output: {value: "Malaysia", done: false}
console.log(countryIterator.next()); //output: {value: "Canada", done: false}
console.log(countryIterator.next()); //output: {value: "Brazil", done: false}
console.log(countryIterator.next()); //output: {value: "Australia", done: false}
console.log(countryIterator.next()); //output: {done: true}

/**
 * Outputs: 
 * Philippines
 * Singapore
 * Malaysia
 * Canada
 * Brazil
 * Australia
 * undefined
 */
for (country of countries) {
    console.log(country)
}

/*End of an iterable protocol*/

现在,我们了解了什么是迭代器和可迭代协议以及forof循环在哪里起作用。为什么不检查并学习可迭代的内置类型?

迭代器内置类型

JavaScript语言具有内置的可迭代的类型,但我们不会面面俱到,但我们要覆盖StringArraySet

有关可迭代的内置类型的完整列表,它们是:

  • String
  • Array
  • Map
  • Set
  • TypedArray
  • 参数对象

检查对象/类型是否实现Symbol.iterator

/** Start of how to check if a type/object implements the Symbol.iterator */

let programmingLanguage = "JavaScript";

//let's us check if the built-in data-type String implements Symbol.iterator
//output: [Symbol(Symbol.iterator)]
Object.getOwnPropertySymbols(programmingLanguage.__proto__);

/** end of how to check if type/object implements the Symbol.iterator */

如您所见,上面的代码示例显示String数据类型确实实现了@@iterator。因此,Strings是可迭代的。

内置迭代器的外观如何?

让我们直接看一个示例,以查看迭代器的外观。

//declare a JavaScript string type
let programmingLanguage = "JavaScript";

//lets invoke the method having an @@iterator property key
let iterator = programmingLanguage[Symbol.iterator]();

//let's check if the @@iterator property function return an iterator
console.log(iterator); //output: StringIterator

如您所见,在上面的示例中,我们已经声明了一个String类型并调用了@@iterator key符号,并检查该函数是否真的返回了迭代器,并且确实返回了StringIterator

迭代器内置类型的快速示例

我们不会详细介绍,因为我们已经讨论了上面的概念。然而,这里的目的是展示如何与内置的迭代器进行交互。

数组迭代器

@@iterator方法是可迭代协议的一部分,该协议定义了如何迭代项。

const pets = ["Andy", "Barkley", "Zandro"];
const petIterator = pets[Symbol.iterator]();
console.log(...petIterator); //outputs: Andy Barkley Zandro

字符串迭代器

[@@iterator]()方法返回迭代String值的迭代器。

const fullName = "Jin Vincent Necesario";
const fullNameIterator = fullName[Symbol.iterator]();
console.log(...fullNameIterator); //outputs: J i n   V i n c e n t   N e c e s a r i o

Set迭代器

 

const myArraySet = new Set(['mango', 'avocado', 'apple', 'apple', 'mango']);
const myArraySetIterator = myArraySet[Symbol.iterator]();

//to show that the initial value of the @@iterator is the same as the initial values
console.log(myArraySet.values()); //output: SetIterator {"mango", "avocado", "apple"}
console.log(myArraySetIterator);  //output: SetIterator {"mango", "avocado", "apple"}

console.log(...myArraySetIterator); //outputs: mango avocado apple 

 

总结

在这篇文章中,我们已经看到了不同的JavaScript循环的优缺点。不仅如此,我们还解决了迭代协议,该协议基本上有两种类型,即迭代器和可迭代协议。最后,我们已经了解了内置迭代器的工作方式。总的来说,这篇文章带您了解了迭代器的难题。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值