博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JavaScript的for从懵懂到辨明
阅读量:6072 次
发布时间:2019-06-20

本文共 4683 字,大约阅读时间需要 15 分钟。

前言

初学JavaScript的时候,知道有各种for的时候,懵懵懂懂,也许是因为没有系统学习的缘故。现在我们把各种for都挨个辨明。

一、for

创建一个循环,包含三个可选表达式。三个可选表达式在圆括号中,由分号分隔。后跟一个循环中执行的语句或块语句。

语法

for ([initialization]; [condition]; [final-expression])	statement复制代码

initialization

初始化语句。可写表达式、赋值语句、变量声明。

condition

循环条件表达式。如果表达式结果为truestatement会被执行。如果表达式结果为false,那么执行流程跳到for语句结构后面的第一条语句。不写表达式,就是永远为true

final-expression

每次循环的最后都要执行的表达式。执行时机是在下一次condition的计算之前。

statement

只要condition的结果为true就会被执行的语句。多条语句使用块语句({...})来包含。没有语句执行,使用空语句(;)。

示例

我想输出五个数字。

for (let i = 0; i < 5; i++)    console.log(i);/*01234*/复制代码

另一种写法输出五个数字。可选的三个表达式,多行语句,需要使用{}包含起来。

for (let i = 0; ; i++) {    if (i >= 5)        break;    console.log(i);}/*01234*/复制代码

注意,如果不写条件表达式,就要确保循环体内能够跳出,防止死循环。break可以跳出循环。

二、for...in

以任意顺序遍历一个对象的可枚举属性。对于每个枚举的属性,...部分都会被执行。

语法

for (variable in object) {...}复制代码

variable

每次迭代的时候,将对象的属性名分配给变量。

object

被迭代枚举的对象。

示例

我想输出对象里所有的属性和值。

let o = {    a: 1,    b: 2,    c: 3};for (const v in o) {    console.log(`o.${v} = ${o[v]}`);}/*o.a = 1o.b = 2o.c = 3*/复制代码

可以看见for...in把所有的可枚举属性都枚举了出来,v的类型是String,所以访问当前遍历到的属性值使用了关联数组o[v]的方式。

for...in在遍历的时候,是以任意顺序遍历的。

let o = [];o[0] = 1;o['one'] = 2;o[2] = 3;for (const v in o) {    console.log(`o[${v}] = ${o[v]}`);}/*o[0] = 1o[2] = 3o[one] = 2*/复制代码

因此当遇到对迭代访问顺序很重要的数组时,最好用整数索引。

我想累加数组所有的成员。

Array.prototype.age = 97;let o = [1,2];let sum = 0;for (const v in o) {    sum += o[v];    console.log(`o[${v}] = ${o[v]}`);}console.log(`sum = ${sum}`);/*o[0] = 1o[1] = 2o[age] = 97sum = 100*/复制代码

很显然这里不符合我们的预期,因为for...in循环语句将返回所有可枚举属性,包括非整数类型的名称和继承的那些。还会获取到原型链上的可枚举属性。

我只想累加自身所有属性。

Array.prototype.age = 97;let arr = [1, 2];let sum = 0;for (const v in arr) {    if (arr.hasOwnProperty(v)) {        sum += arr[v];    }    console.log(`arr[${v}] = ${arr[v]}`);}console.log(`sum = ${sum}`);/*o[0] = 1o[1] = 2o[age] = 97sum = 3*/复制代码

如果你只要考虑对象本身的属性,而不是它的原型,那么使用Object.getOwnPropertyNames()或执行Object.prototype.hasOwnProperty()来确定某属性是否是对象本身的属性(也能使用propertyIsEnumerable)。

三、Array.prototype.forEach()

对数组的每个元素执行一次提供的函数。返回值为undefined

语法

Array.forEach(callback[, thisArg])复制代码

callback

为数组每个元素执行的函数,这个函数接受三个参数。

currentValue

数组中正在处理的当前元素值。

index

数组中正在处理的当前元素的索引。

array

forEach()方法正在操作的数组。

thisArg

可选参数。当执行回调 函数时用作this的值(参考对象)。

示例

我想输出所有元素。

function logArrayElements(element, index, array) {    console.log(`a[${index}] = ${element}`);}[4, 2, 3].forEach(logArrayElements);/*a[0] = 4a[1] = 2a[2] = 3*/复制代码

forEcah()会跳过已经删除或者为初始化的项(但不包括那些值为undefined的项,例如在稀疏数组上)。

function logArrayElements(element, index, array) {    console.log(`a[${index}] = ${element}`);}[4, , 3].forEach(logArrayElements);[1, undefined, 3].forEach(logArrayElements);/*a[0] = 4a[2] = 3a[0] = 1a[1] = undefineda[2] = 3*/复制代码

没有办法终止会跳出forEcah()循环,除了抛出一个异常。

function logArrayElements(element, index, array) {    console.log(`a[${index}] = ${element}`);    break;}[1, 2, 3].forEach(logArrayElements);/*Uncaught SyntaxError: Illegal break statement    at Array.forEach (
) at
:5:11*/复制代码

使用return也无法中止循环。

使用thisArg,举个勉强的例子。通过自定义的add()方法,计算所添加数组的和sum和成员数count

function Counter() {    this.sum = 0;    this.count = 0;}Counter.prototype.add = function(array) {    array.forEach(function(element) {        this.sum += element;        ++this.count;    }, this);};let obj = new Counter();obj.add([1, 3, 5, 7]);console.log(obj.count);  // 4 === (1+1+1+1)console.log(obj.sum);  // 16 === (1+3+5+7)/*416*/复制代码

注意:如果使用箭头函数表达式传入函数参数,thisArg参数会被忽略,因为箭头函数在词法上绑定了this值。

如果数组在迭代时被修改了,则其他元素会被跳过。

let words = ["one", "two", "three", "four"];words.forEach(function(word) {  console.log(word);  if (word === "two") {    words.shift();  }});/*onetwofour*/复制代码

当到达包含值"two"的项时,整个数组的第一个项被移除了,这导致所有剩下的项前移了一个位置。因为元素"four"现在在数组更前的位置,"three"会被跳过。forEach()不会在迭代之前创建数组的副本。

四、for...of

for...of语句在可以迭代的对象(ArrayMapSetStringTypedArrayarguments对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。

语法

for (variable of iterable) {    ...}复制代码

variable

在每次迭代中,将不同属性的值分配给变量。

iterable

被迭代枚举其属性的对象。

示例

迭代Array

let a = [10, 20, 30];for (let v of a) {    console.log(v);}/*102030*/复制代码

迭代String

let s = 'Tang';for (let v of s) {    console.log(v);}/*Tang*/复制代码

迭代arguments

(function() {    for (let v of arguments) {        console.log(v);    }})(1, 2, 3);/*123*/复制代码

区别

无论是for...in还是for...of语句都是迭代一些东西。它们之间的主要区别在于它们的迭代方式。

for...in语句以原始插入顺序迭代对象的可枚举属性。

for...of语句遍历可迭代对象定义要迭代的数据。

以下示例显示了与Array一起使用时,for...of循环和for...in循环之间的区别。

Object.prototype.objCustom = function() {}; Array.prototype.arrCustom = function() {};let iterable = [3, 5, 7];iterable.foo = 'hello';for (let i in iterable) {  console.log(i);}/*012fooarrCustomobjCustom*/for (let i in iterable) {  if (iterable.hasOwnProperty(i)) {    console.log(i);  }}/*012foo*/for (let i of iterable) {  console.log(i);}/*357*/复制代码

转载地址:http://jsbgx.baihongyu.com/

你可能感兴趣的文章
linux环境配置
查看>>
ASP.NET MVC中从前台页面视图(View)传递数据到后台控制器(Controller)方式
查看>>
lintcode:next permutation下一个排列
查看>>
python 递归
查看>>
一个想法(续二):换个角度思考如何解决IT企业招聘难的问题!
查看>>
tomcat指定配置文件路径方法
查看>>
linux下查看各硬件型号
查看>>
对象合成复用之策略模式
查看>>
linux命令之tail
查看>>
epoll的lt和et模式的实验
查看>>
Flux OOM实例
查看>>
基于智能家居场景的POALRDB性能体验
查看>>
无人餐厅喜忧参半,要成还得看“暖科技”?
查看>>
盒马鲜生To C,美菜网To B:生鲜独角兽的不同成长之路
查看>>
影响网页渲染的关键
查看>>
如何快速更新当前项目到最新的Angular稳定版本
查看>>
19.gdb调试
查看>>
koa源码阅读[0]
查看>>
云栖科技评论 | “虚拟X”占领真实世界
查看>>
Guava 27.1 正式发布,Google 的 Java 核心工具库
查看>>