JavaScript指南第5部分:数组和循环

今天,在JavaScript课程翻译的第五部分中,我们将讨论数组和循环。 数组用于解决许多问题。 通常使用循环来处理数组。

第1部分:第一个程序,语言功能,标准
第2部分:代码样式和程序结构
第3部分:变量,数据类型,表达式,对象
第4部分:功能
第5部分:数组和循环
第6部分:异常,分号,通配符文字
第7部分:严格模式,此关键字,事件,模块,数学计算
第8部分:ES6功能概述
第9部分:ES7,ES8和ES9标准概述



数组


数组(类型为Array对象)与语言的其他机制一起发展。 它们是编号值的列表。

数组的第一个元素的索引(键)为0;许多编程语言都使用此方法。

在本节中,我们将考虑使用数组的现代方法。

▍初始化数组


这是一些初始化数组的方法。

 const a = [] const a = [1, 2, 3] const a = Array.of(1, 2, 3) const a = Array(6).fill(1) //   ,   6 ,  1 

为了访问数组的单个元素,请使用包含方括号的结构,该方括号包含数组元素的索引。 数组元素可以读取或写入。

 const a = [1, 2, 3] console.log(a) //[ 1, 2, 3 ] const first = a[0] console.log(first) //1 a[0] = 4 console.log(a) //[ 4, 2, 3 ] 

不建议使用用于声明数组的Array构造函数。

 const a = new Array() //  const a = new Array(1, 2, 3) //  

仅在声明类型数组时才应使用此方法。

▍获取数组的长度


为了找出数组的长度,您需要引用其length属性。

 const l = a.length 

using使用every()方法检查数组


every()数组方法可用于使用特定条件来组织对其所有元素的验证。 如果数组的所有元素都满足条件,则该函数将返回true ,否则将返回false

该方法传递给一个函数,该函数采用参数currentValue (当前数组元素的index ), index (当前数组元素的索引)和array (数组本身)。 它也可以采用一个可选值,在执行传递给它的函数时用作this值。
例如,检查数组所有元素的值是否大于10。

 const a = [11, 12, 13] const b = [5, 6, 25] const test = el => el > 10 console.log(a.every(test)) //true console.log(b.every(test)) //false 

在这里,在test()函数中,我们只对传递给它的第一个参数感兴趣,因此我们声明它,仅指定el参数,相应的值将落入该参数。

using使用some()方法检查数组


此方法与every()方法非常相似,但是如果数组的至少一个元素满足传递给它的函数指定的条件,则它返回true

using使用map()方法基于现有数组创建数组


数组的map()方法允许您遍历数组,对传递给此方法的每个元素应用一个函数,该函数转换该元素并根据接收到的值创建新数组。 例如,这里是获取新数组的方法,这是将原始数组的所有元素乘以2的结果。

 const a = [1, 2, 3] const double = el => el * 2 const doubleA = a.map(double) console.log(a) //[ 1, 2, 3 ] console.log(doubleA) //[ 2, 4, 6 ] 

using使用filter()方法过滤数组


filter()方法类似于map()方法,但是它允许您创建新数组,其中仅包含满足传递给函数的filter()方法指定的条件的原始数组的那些元素。

▍reduce()方法


reduce()方法允许您将给定函数应用于累加器和数组的每个值,从而将数组缩小为单个值(该值可以具有基本类型或对象类型)。 此方法具有转换功能和可选的初始电池值。 考虑一个例子。

 const a = [1, 2, 3, 4].reduce((accumulator, currentValue, currentIndex, array) => { return accumulator * currentValue }, 1) console.log(a) //24 // 1: 1 * 1 = 1 // 2: 1 * 2 = 2 // 3: 2 * 3 = 6 // 4: 6 * 4 = 24 

在这里,我们正在寻找使用文字描述的数组所有元素的乘积,并设置累加器1的初始值。

using使用forEach()方法枚举数组


数组的forEach()方法可用于遍历数组的值并对其执行某些操作,这由传递给该方法的函数指定。 例如,我们一次在控制台中显示数组的元素。

 const a = [1, 2, 3] a.forEach(el => console.log(el)) //1 //2 //3 

如果在遍历数组时需要停止或中断循环,则在使用forEach()时必须抛出异常。 因此,如果在解决某个问题的过程中可能有必要中断循环,则最好选择其他方式迭代数组元素。

using使用运算符的for ...选择数组


运算符的for...of出现在ES6标准中。 它允许您遍历可迭代对象(包括数组)。 这是使用方法。

 const a = [1, 2, 3] for (let v of a) { console.log(v) } //1 //2 //3 

在循环的每次迭代中,数组a的下一个元素进入变量v

using使用for语句枚举数组


for语句使您可以组织循环,尤其是可以通过按索引访问数组的元素来循环(或初始化)数组。 通常,使用循环计数器获得下一个元素的索引。

 const a = [1, 2, 3] for (let i = 0; i < a.length; i += 1) { console.log(a[i]) } //1 //2 //3 

如果在循环执行期间需要跳过其迭代,则可以使用continue命令。 您可以使用break命令提前终止循环。 例如,如果在某个函数中的循环中使用return命令,则循环和函数将结束,并且return的值将转到调用该函数的位置。

▍方法@@迭代器


此方法出现在ES6标准中。 它使您能够获得所谓的“对象的迭代器”-在这种情况下,您可以通过该对象组织数组元素的迭代。 可以使用Symbol(这样的符号称为“已知符号”) Symbol.iterator获得数组迭代器。 收到迭代器后,您可以访问其next()方法,该方法每次调用都会返回一个包含数组下一个元素的数据结构。

 const a = [1, 2, 3] let it = a[Symbol.iterator]() console.log(it.next().value) //1 console.log(it.next().value) //2 console.log(it.next().value) //3 

如果在到达数组的最后一个元素之后调用next()方法,它将作为元素的值返回undefinednext()方法返回的对象包含valuedone属性。 在到达数组的最后一个元素之前, done属性的值为false 。 在我们的例子中,如果我们第四次调用它,那么它将返回{ value: undefined, done: true }对象,而在前三个调用中,该对象看起来像{ value: , done: false }

entries()数组方法返回一个迭代器,该迭代器允许您迭代数组的键值对。

 const a = [1, 2, 3] let it = a.entries() console.log(it.next().value) //[0, 1] console.log(it.next().value) //[1, 2] console.log(it.next().value) //[2, 3] 

keys()方法允许您迭代数组的键。

 const a = [1, 2, 3] let it = a.keys() console.log(it.next().value) //0 console.log(it.next().value) //1 console.log(it.next().value) //2 

Elements将元素添加到数组的末尾


要将元素添加到数组的末尾,请使用push()方法。

 a.push(4) 

elements将元素添加到数组的开头


要将元素添加到数组的开头,请使用unshift()方法。

 a.unshift(0) a.unshift(-2, -1) 

▍删除数组元素


您可以使用pop()方法从数组的末尾删除元素,同时返回该元素。

 a.pop() 

同样,使用shift()方法,可以从数组的开头删除元素。

 a.shift() 

使用splice()方法可以完成同一件事,但是已经指示了元素的移除位置及其数量。

 a.splice(0, 2) //    2     a.splice(3, 2) //    2 ,    3 

▍删除数组元素并插入其他元素


为了使用某些操作删除数组中的某些元素并插入其他元素,使用了熟悉的splice()方法。

例如,在这里我们删除从索引2开始的数组的3个元素,然后在同一位置添加另外两个元素:

 const a = [1, 2, 3, 4, 5, 6] a.splice(2, 3, 'a', 'b') console.log(a) //[ 1, 2, 'a', 'b', 6 ] 

▍合并多个数组


要组合多个数组,可以使用concat()方法,该方法返回一个新数组。

 const a = [1, 2] const b = [3, 4] const c = a.concat(b) console.log(c) //[ 1, 2, 3, 4 ] 

in查找数组中的项目


在ES5标准中,出现了indexOf()方法,该方法返回所需数组元素首次出现的索引。 如果在数组中找不到该元素,则返回-1

 const a = [1, 2, 3, 4, 5, 6, 7, 5, 8] console.log(a.indexOf(5)) //4 console.log(a.indexOf(23)) //-1 

lastIndexOf()方法返回该元素在数组中最后一次出现的索引;如果未找到该元素,则返回-1

 const a = [1, 2, 3, 4, 5, 6, 7, 5, 8] console.log(a.lastIndexOf(5)) //7 console.log(a.lastIndexOf(23)) //-1 

在ES6中,出现了数组的find()方法,该方法使用传递给它的函数执行数组搜索。 如果函数返回true ,则该方法返回找到的第一个元素的值。 如果找不到该项目,则该函数将返回undefined

它的用法如下所示。

 a.find(x => x.id === my_id) 

在此,在包含对象的数组中,搜索一个元素,其id属性等于指定的元素。

findIndex()方法类似于find() ,但是它返回找到或undefined元素的索引。

在ES7中,出现了includes()方法,该方法使您可以检查数组中是否存在某个元素。 它返回truefalse ,找到或没有找到程序员感兴趣的元素。

 a.includes(value) 

使用此方法,可以从调用此方法时指定的索引开始,检查是否存在某些元素,而不是整个数组,而只是部分数组。 使用此方法的第二个可选参数指定索引。

 a.includes(value, i) 

▍获取数组的片段


为了获得该数组的某些片段的副本作为新数组,可以使用slice()方法。 如果不带参数调用此方法,则返回的数组将是原始数组的完整副本。 它带有两个可选参数。 第一个设置片段的起始索引,第二个设置结束。 如果未指定结束索引,则将数组从指定的开始索引复制到结束。

 const a = [1, 2, 3, 4, 5, 6, 7, 8, 9] console.log(a.slice(4)) //[ 5, 6, 7, 8, 9 ] console.log(a.slice(3,7)) //[ 4, 5, 6, 7 ] 

▍排序数组


为了按字母顺序( 0-9A-Za-z )组织数组元素的sort() ,使用了sort()方法,而没有向其传递参数。

 const a = [1, 2, 3, 10, 11] a.sort() console.log(a) //[ 1, 10, 11, 2, 3 ] const b = [1, 'a', 'Z', 3, 2, 11] b.sort() console.log(b) //[ 1, 11, 2, 3, 'Z', 'a' ] 

您可以将函数传递给此方法以设置排序顺序。 为了比较两个元素,该函数接受参数ab 。 如果b按某个标准小于b ,则返回负数;如果相等,则返回0;如果b大于b ,则返回正数。 当编写类似的函数对数字数组进行排序时,它可以返回减去ab的结果。 因此,返回对表达式a - b求值的结果意味着对数组进行升序排序,返回对表达式b - a求值的结果将对数组进行降序排序。

 const a = [1, 10, 3, 2, 11] console.log(a.sort((a, b) => a - b)) //[ 1, 2, 3, 10, 11 ] console.log(a.sort((a, b) => b - a)) //[ 11, 10, 3, 2, 1 ] 

为了反转数组元素的顺序,可以使用reverse()方法。 就像sort() ,它修改为其调用的数组。

▍获取数组的字符串表示形式


要获取数组的字符串表示形式,可以使用其toString()方法。

 a.toString() 

join()方法给出了类似的结果,该方法无需参数即可调用。

 a.join() 

作为参数,您可以传递分隔符元素。

 const a = [1, 10, 3, 2, 11] console.log(a.toString()) //1,10,3,2,11 console.log(a.join()) //1,10,3,2,11 console.log(a.join(', ')) //1, 10, 3, 2, 11 

▍创建数组的副本


要通过将原始数组的值复制到新数组中来创建数组的副本,可以使用Array.from()方法。 它也适用于从类似数组的对象(例如,从字符串)创建数组。

 const a = 'a string' const b = Array.from(a) console.log(b) //[ 'a', ' ', 's', 't', 'r', 'i', 'n', 'g' ] 

Array.of()方法还可用于复制数组,以及从各种元素“组合”数组。 例如,要将一个数组的元素复制到另一个数组,可以使用以下构造。

 const a = [1, 10, 3, 2, 11] const b = Array.of(...a) console.log(b) // [ 1, 10, 3, 2, 11 ] 

copyWithin()方法用于将数组的元素复制到此数组本身的特定位置。 它的第一个参数指定目标位置的初始索引,第二个参数指定元素源的位置的初始索引,第三个参数(可选)指示元素源的位置的最终索引。 如果未指定,则将从源位置的初始索引到数组末尾的所有内容都复制到数组中的指定位置。

 const a = [1, 2, 3, 4, 5] a.copyWithin(0, 2) console.log(a) //[ 3, 4, 5, 4, 5 ] 

周期数


说到上面的数组,我们已经遇到了一些组织循环的方法。 但是,JavaScript中的循环不仅用于处理数组,而且我们考虑的范围远非所有类型。 因此,现在我们将花费一些时间来讨论在JavaScript中组织循环的不同方法并讨论其功能。

▍for循环


考虑应用此循环的示例。

 const list = ['a', 'b', 'c'] for (let i = 0; i < list.length; i++) { console.log(list[i]) //,     console.log(i) // } 

如前所述,您可以使用break命令中断此类循环的执行,并且可以跳过当前迭代,并使用continue命令直接转到下一个迭代。

▍for每个周期


我们还讨论了这个周期。 这是使用数组迭代数组的示例。

 const list = ['a', 'b', 'c'] list.forEach((item, index) => { console.log(item) // console.log(index) // }) //     ,      list.forEach(item => console.log(item)) 

回想一下,要中断这样一个循环,有必要抛出一个异常,也就是说,如果在使用一个循环时可能需要中断它,那么最好选择其他循环。

▍做... while循环


这就是所谓的“后置条件周期”。 在检查结束循环的条件之前,将至少执行一次这样的循环。

 const list = ['a', 'b', 'c'] let i = 0 do { console.log(list[i]) // console.log(i) // i = i + 1 } while (i < list.length) 

可以使用break命令将其中断,您可以使用continue命令继续进行下一次迭代。

▍while循环


这就是所谓的“预处理循环”。 如果在循环的入口处继续循环的条件为假,则即使执行一次也不会执行。

 const list = ['a', 'b', 'c'] let i = 0 while (i < list.length) { console.log(list[i]) // console.log(i) // i = i + 1 } 

▍for ...循环中


通过此循环,可以按对象的名称遍历对象的所有枚举属性。

 let object = {a: 1, b: 2, c: 'three'} for (let property in object) { console.log(property) //  console.log(object[property]) //  } 

▍的...个周期


for...of循环结合了forEach循环的便利性和使用常规工具中断其操作的能力。

 //  for (const value of ['a', 'b', 'c']) { console.log(value) // } //       `entries()` for (const [index, value] of ['a', 'b', 'c'].entries()) { console.log(index) // console.log(value) // } 

请注意,在这里,在循环头中使用了const关键字,而不是如您所期望的那样使用let 。 如果循环块内的变量不需要重新分配,那么const非常适合我们。
如果我们比较for...infor...of循环,就会发现for...in迭代属性的名称,而for...of of-属性for...of值。

循环和范围


使用循环和变量作用域,有一种JavaScript功能可能会导致开发人员遇到一些问题。 为了解决这些问题, let谈谈循环,作用域以及varlet关键字。

考虑一个例子。

 const operations = [] for (var i = 0; i < 5; i++) { operations.push(() => {   console.log(i) }) } for (const operation of operations) { operation() } 

该循环执行5次迭代,每一次迭代都会向operations数组添加一个新函数。 此功能在控制台中显示循环计数器i的值。 在将函数添加到数组之后,我们遍历该数组并调用作为其元素的函数。

通过执行这样的代码,您可以预期如下所示的结果。

 0 1 2 3 4 

但实际上,他推断出以下情况。

 5 5 5 5 5 

为什么会这样呢? 事实是,作为循环计数器,我们使用使用var关键字声明的变量。

由于此类变量的声明位于作用域的顶部,因此上述代码与以下代码相似。

 var i; const operations = [] for (i = 0; i < 5; i++) { operations.push(() => {   console.log(i) }) } for (const operation of operations) { operation() } 

结果,在循环的for...of循环中,我们遍历数组,变量i仍然可见,为5,结果,在所有函数中引用i ,我们打印数字5。

如何更改程序的行为,使其能够执行预期的工作?

解决此问题的最简单方法是使用let关键字。 正如我们已经说过的那样,它出现在ES6中,它的使用使您可以摆脱var的一些奇怪特性。

特别是,在上面的示例中,将var更改为let就足够了,并且一切都会按预期进行。

 const operations = [] for (let i = 0; i < 5; i++) { operations.push(() => {   console.log(i) }) } for (const operation of operations) { operation() } 

现在,在循环的每次迭代中,添加到operations数组的每个函数都会获得自己的i副本。 请记住,在这种情况下您不能使用const关键字,因为循环中i的值会更改。

解决此问题的另一种方法是使用IIFE,该方法通常在ES6标准之前使用,而当let关键字不存在时。

使用这种方法, i的值存储在闭包中,并且IIFE返回并可以访问闭包的函数进入数组。 必要时可以执行此功能。 这是它的外观。

 const operations = [] for (var i = 0; i < 5; i++) { operations.push(((j) => {   return () => console.log(j) })(i)) } for (const operation of operations) { operation() } 

总结


今天,我们讨论了JavaScript中的数组和循环。 下一篇文章的主题是异常处理,分号使用模式和模板文字。

亲爱的读者们! 您最常使用哪种方法在JavaScript中使用数组?

Source: https://habr.com/ru/post/zh-CN430380/


All Articles