资讯专栏INFORMATION COLUMN

JavaScript的for从懵懂到辨明

loonggg / 1898人阅读

摘要:对于每个枚举的属性,部分都会被执行。被迭代枚举的对象。三对数组的每个元素执行一次提供的函数。没有办法终止会跳出循环,除了抛出一个异常。当到达包含值的项时,整个数组的第一个项被移除了,这导致所有剩下的项前移了一个位置。

前言

初学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);
/*
0
1
2
3
4
*/

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

for (let i = 0; ; i++) {
    if (i >= 5)
        break;
    console.log(i);
}
/*
0
1
2
3
4
*/

注意,如果不写条件表达式,就要确保循环体内能够跳出,防止死循环。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 = 1
o.b = 2
o.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] = 1
o[2] = 3
o[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] = 1
o[1] = 2
o[age] = 97
sum = 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] = 1
o[1] = 2
o[age] = 97
sum = 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] = 4
a[1] = 2
a[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] = 4
a[2] = 3
a[0] = 1
a[1] = undefined
a[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)
/*
4
16
*/

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

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

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

当到达包含值"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);
}
/*
10
20
30
*/
迭代String
let s = "Tang";
for (let v of s) {
    console.log(v);
}
/*
T
a
n
g
*/
迭代arguments
(function() {
    for (let v of arguments) {
        console.log(v);
    }
}
)(1, 2, 3);
/*
1
2
3
*/
区别
无论是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);
}
/*
0
1
2
foo
arrCustom
objCustom
*/

for (let i in iterable) {
  if (iterable.hasOwnProperty(i)) {
    console.log(i);
  }
}
/*
0
1
2
foo
*/

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/108069.html

相关文章

  • 编译原理实战入门:用 JavaScript 写一个简单四则运算编译器(四)结语

    摘要:四则运算编译器,虽然说功能很简单,只能编译四则运算表达式。再复杂的编译器再简单的编译器,功能上是差不多的,只是复杂的编译器实现上会更困难。每一章都是理论与实践结合的经典,从计算机硬件知识到软件体系,再到编译原理和操作系统。 四则运算编译器,虽然说功能很简单,只能编译四则运算表达式。但是编译原理前端部分几乎都有涉及,词法分析,语法分析,还有代码生成。 再复杂的编译器、再简单的编译器,功能...

    chemzqm 评论0 收藏0
  • HTTPS是什么?网站部署HTTPS注意事项有哪些!

    摘要:证书证书被称为扩展验证型证书,是因为申请证书不仅需要验证域名所有权,按规定提交企业真实身份验证材料,还要求提供更加详细的企业信息如具体营业地址等信息,并提供具有法律效力的证明文件如律师函等。以最大限度降低用户访问不安全网站的风险。 什么是 HTTPS? HTTPS(超文本传输安全协议)是一种互联网通信安全协议,它确保在用户的计算机与网站终端服务器之间传递的数据的完整性和机密性。所以,为...

    Little_XM 评论0 收藏0
  • Ruff:为物联网而生

    摘要:一个开放高效敏捷的物联网应用开发平台,就此诞生,也被称为全球最好用的物联网操作系统。区块链技术再加码,物联网生态持续精进随着区块链技术的出现及持续升温,如今区块链已经成为大众广泛关注的一个话题。 showImg(https://segmentfault.com/img/bV8bKH?w=2121&h=1414); 世界正在发生改变。 在无锡,中国第一个物联网之城——鸿山小镇已经悄然诞生...

    daydream 评论0 收藏0
  • Windows Server Version 1709 管理之入门篇

    摘要:而且在这一版本里,微软没有提供图形界面到的转换。之父,在加入微软之前是搞的。在发明了这后,直接就晋升为微软的并且主导了的架构设计跑远了,回来。通过远程在客户端机器上运行以下命令即可是客户端机器的文件位置。 摘要: 相信有部分同学们会有这样的体验,在公有云上购买了Windows Server Version 1709数据中心版的虚拟机,通过远程连接进去之后,里面全是黑乎乎的一个命令行,其...

    pakolagij 评论0 收藏0
  • 你还认为JS中万物皆对象?

    摘要:这也解答了我曾经的一个疑问同样的道理,在调用属性的瞬间,也是使用先来实例化一个对象,所以那一瞬间他们的构造函数以及原型对象是相同的,但也仅仅是那一瞬间。 经常在国内的各大网站博客上看到一句话,叫做JS中万物皆对象,那是否真是如此? 那么,我们先来捋一捋JS中的数据类型,JS中的数据类型有下面几种 Undefined Null Boolean Number String Symbol ...

    levinit 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<