面试题二:谈一谈对JavaScript中for in与for of的理解

2023-11-18

for in

for...in 语句以任意顺序迭代一个对象的除Symbol以外的可枚举属性,包括继承的可枚举属性。

在没有深度了解过for in的作用前,我只停留在它能够遍历对象键和数组下标的的层面上,那现在就来探究什么是一个对象中的Symbol类型键(ES6之后新加的)和可枚举属性

JS中的对象类型包括Object,Array,Function,Date,Math....

本文主要用Object和Array来进行实践

首先,先来看一段代码:

const arr = [5, 4, 3, 2, 1];
 
const obj = {
  mark: "mark-v",
  jack: "jack-v",
  amy: "amy-v",
};
 
for (const i in arr) {  // 输出 0 1 2 3 4
  console.log(i);
}
 
for (const i in obj) { // 输出 mark jack amy
  console.log(i);
}
 

我想必大家都很清楚输出的结果,如果是遍历数组则输出数组对应的下标,如果是遍历对象则输出对象的键名

继续提高难度,再看下面的代码:

const arr = [1, 2, 3, 4, 5];
 
arr.value = "array"; // 给数组对象上添加一个value属性
 
const s1 = Symbol("symbol1"); // 定义两个symbo类型的值
const s2 = Symbol("symbol2");
 
const obj = {
  mark: "mark-v",
  jack: "jack-v",
  amy: "amy-v",
  [s1]: "symbol1-v", // 给obj初始化这两个symbol键值
  [s2]: "symbol2-v",
};
 
// 给obj原型上添加一个属性
Object.getPrototypeOf(obj).myProto = 'proto'
 
console.log(obj);
// 打印 {
//  mark: 'mark-v',
//  jack: 'jack-v',
//  amy: 'amy-v',
//  [Symbol(symbol1)]: 'symbol1-v',
//  [Symbol(symbol2)]: 'symbol2-v'
// }
 
for (const i in arr) {
  console.log(i); // 输出 0 1 2 3 4 value myProto
}
 
for (const i in obj) {
  console.log(i); // 输出 mark jack amy myProto
}
 
 

从上面输出结果可以看出,无论是数组元素或者对象上的键值,都是可枚举属性,并且可以看出Symbol类型的键,虽然定义在了对象中,但是for in会跳过Symbol类型键的遍历

并且for in还会往原型链上遍历,那为什么遍历obj和arr都会输出 'myProto' 呢,那是因为obj是一个对象,默认的构造函数是Object(),而arr是一个数组对象,默认的构造函数是Array(),但是Array的prototype原型属性本质上也是一个对象,所以Array原型的原型就是Object上的prototype属性
 

Object.getPrototypeOf(obj) === Object.getPrototypeOf(Object.getPrototypeOf(arr)) // true

所以arr和obj的原型链是有交点了,也就解释了他们都能遍历到myProto

我们也可以单纯认为一个对象中的Symbol类型的键对应的属性不是一个可枚举属性,但是其实它依旧是一个可枚举属性,只是被跳过了而已,这段话说起来有点绕,不过往下看就清楚了。

既然for in的遍历绕不开可枚举属性,那什么是可枚举属性呢,MDN的解释如下:
 

可枚举属性是指那些内部“可枚举”标志设置为 true 的属性,对于通过直接的赋值和属性初始化的属性,该标识值默认为即为 true,对于通过 Object.defineProperty 等定义的属性,该标识值默认为 false。可枚举的属性可以通过 for...in 循环进行遍历(除非该属性名是一个 Symbol)。属性的所有权是通过判断该属性是否直接属于某个对象决定的,而不是通过原型链继承的。

从上面可以得出,

可枚举属性是对象内部那些“可枚举”标志设置为true的属性
通过字面量定义的属性,可枚举标识默认为true,对于通过Object.defineProperty等定义的属性,该标识默认为flase
for in循环只会对对象中的可枚举属性进行遍历,并且跳过属性名是Symbol类型的属性
for in只会遍历对象本身上面的可枚举属性
请看如下代码:
 

const s1 = Symbol("symbol1");
const s2 = Symbol("symbol2");
// 以下字面量初始化和赋值的属性可枚举标识都为true
const obj = {          // 方式一
  mark: "mark-v",
};
obj.jack = "jack-v"     // 方式二
obj['amy'] = 'amy-v'    // 方式三
 
Object.defineProperty(obj, "kong", {
  value: "kong-v",
  enumerable: false,   // 可枚举标识,默认为 false
});
 
Object.defineProperty(obj, "john", {
  value: "john-v",
  enumerable: true,   // 设置为 true
});
// 通过Object.defineProperty设置Symbol类型键
Object.defineProperty(obj, s1, {
  value: "s1-v",
  enumerable: false,   
});
// 通过Object.defineProperty设置Symbol类型键,并且可枚举标识为true
Object.defineProperty(obj, s2, {
  value: "s2-v",
  enumerable: true,
});
 
console.log(obj);
// 打印 {
//   mark: 'mark-v',
//   jack: 'jack-v',
//   amy: 'amy-v',
//   john: 'john-v',
//   [Symbol(symbol2)]: 's2-v'
// }
 
for (const i in obj) {
  console.log(i);     // 输出 mark jack amy john
}
 

 

 

上面代码我们首先是创建了一个obj对象,有一个初始化属性mark,然后通过字面量方式定义了两个新属性,并且通过Object.defineProperty定义了2个string类型和2个symbol类型的键值,都各有一个可枚举和不可枚举标识

可以看到console.log打印obj,只包含了可枚举的属性,也就是说可枚举标识为false的属性是不会出现在整个obj对象的打印内容中,也就验证了就算Symbol类型键的属性可枚举标识为true,也是会被for in跳过。
额外拓展

const arr = [1, 2, 3, 4, 5];
// 设置arr对象属性0的值为100,不可枚举,不可修改
Object.defineProperty(arr, 0, {
  value: 100,
  enumerable: false,
  writable: false,
});
 
arr[0] = 1 // 尝试修改下标0的值
 
console.log(arr);  // 打印 [ 100, 2, 3, 4, 5 ]
 
for (const i in arr) {
  console.log(i);    // 输出 1 2 3 4
}

可以看到数组的下标也是数组对象的属性,设置了下标0不可枚举和不可修改后,for in遍历不到下标0,并且也修改不了下标0的值,但是好玩的是打印数组还是会完整打印出每个元素。

for of 

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

for of是ES6新出的一个用于遍历可迭代对象(实现了Iterator接口)的语法

// 迭代数组
const array = [10, 20, 30];
for (const value of array) {
    console.log(value); // 输出 10 20 30
}
 
// 迭代String
const string = 'kong'
for (const s of string) {
    console.log(s); // 输出 k o n g
}
 
// 迭代map
const map = new Map([["a", 1], ["b", 2], ["c", 3]]);
for (const item of map) {
  console.log(item);  // 输出  [ 'a', 1 ]  [ 'b', 2 ]  [ 'c', 3 ]
}
 
// 迭代set
const set = new Set([1, 1, 2, 2, 3, 3]);
for (let item of set) {
  console.log(item);  // 输出 1 2 3
}

 关闭迭代器

对于for...of的循环,可以由 breakthrow 或 return 终止。在这些情况下,迭代器关闭。

return情况

const array = [10, 20, 30];
for (const value of array) {
    console.log(value); // 输出 10
    return
}
// 下面代码不会执行,前面已经return
const string = 'kong'
for (const s of string) {
    console.log(s); 
}

 throw情况

const array = [10, 20, 30];
for (const value of array) {
  console.log(value); // 输出 10
  throw new Error()
}
// 不执行下面代码,上面已经抛错
const string = "kong";
for (const s of string) {
  console.log(s); 
}

对于for...in的循环,以上的中断方式也适用

总结
for...in适用于对象上面可枚举属性的遍历,并且只遍历非Symbol类型键以及对象自身以及原型链上的可枚举属性
for...of适用于实现了Iterator接口的对象(也称作可迭代对象)的遍历,其遍历方式由自身实现,例如对于数组是遍历其每个下标对应元素的值,对于Map遍历值为键值对组成的数组
 

各位大咖大牛,点个赞支持下,感谢!!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

面试题二:谈一谈对JavaScript中for in与for of的理解 的相关文章

  • 声明为对象文字与函数的剔除视图模型之间的区别

    在knockout js中 我看到视图模型声明为 var viewModel firstname ko observable Bob ko applyBindings viewModel or var viewModel function
  • 检测单选按钮/复选框状态的变化

    我需要可靠地检测页面上单选按钮 复选框的状态变化 以便查看表单是否被修改 现在 这是一个完全独立的脚本 我无法修改任何控制表单的内容 目前 我只能看到两种方法 onchange事件处理程序 有助于处理文本框 文本区域和选择 但不会针对复选框
  • 为什么省略分号会破坏这段代码?

    或者换句话说 为什么分号插入失败 导致下面的代码被破坏 function Foo Foo prototype bar function console log bar lt missing semicolon function Foo pr
  • 有没有办法动态更改 jqGrid 的单元格值?

    这个问题可能已经被问过很多次了 但我想知道是否可以动态更改 jqgrid 的单元格值 我基本上有一个网格 它通过 JSON 字符串加载数据 在特定列的某些行上 该值可能为 null 因此 预先知道哪个行 ID 是一个问题 然后能够将 nul
  • JavaScript 字符串中的脚本标签[重复]

    这个问题在这里已经有答案了 我遇到一个问题 即 JavaScript 中带引号的字符串内有结束脚本标记 并且它正在杀死脚本 我认为这不是预期的行为 可以在这里看到这样的示例 http jsbin com oqepe edit http js
  • 获取被调用的 javascript 文件的查询字符串

    是否可以在调用的 javascript 文件上使用 javascript 获取查询参数 如下所示 in html in file js console log this location query 这是否可能以某种方式实现 或者我必须使用
  • jQuery:查找具有特定自定义属性的元素

    我只想找到具有特定自定义属性值的元素 例如 我想找一个div其具有属性data divNumber 6 var number 6 var myDiv data divNumber number 我尝试使用http api jquery co
  • 如何在React Native的MapView中设置标记

    我想在React Native中的MapView上设置一个标记 但是通过官方文档找不到任何信息MapView https facebook github io react native docs mapview html content 如
  • 从选择 onChange 调用 javascript 函数 [重复]

    这个问题在这里已经有答案了 所以我有一个简单的 HTML 选择框和一个 javascript 警报功能 我希望选择框有一个 onchange 事件来调用 javascript 警报函数 这是我到目前为止所拥有的 HTML div Type
  • 使用 jQuery inputmask 插件范围 0-100

    如何创建 0 到 100 范围内的掩码 document ready function masked inputmask 您可以使用jquery inputmask regex extensions js为了那个原因 你可以找到带有所有扩展
  • onClick 处理程序在每个渲染周期触发

    我有这样的默认状态 this state selectedTab tab1 then 我的渲染方法是这样的 render const selectedTab this state return li tab1 li li tab2 li d
  • websockets 如何处理同一浏览器的两个选项卡

    I have 1 个 PHP 服务器 提供 http 请求 和 1 node js 发布更新的数据消息 每个连接都带有 websocket php 服务器设置其 cookie 在一个浏览器中 此 cookie 可在所有选项卡中使用 当浏览器
  • 通过 JavaScript 单击按钮/页面提交

    我想了解 asp net 框架如何知道何时单击了按钮 因此一旦收到请求 就会在服务器上触发其单击事件 我需要了解它是如何工作的 因为我想从 JavaScript 触发按钮的服务器单击事件 我能够从 JavaScript 执行页面提交 doc
  • 使用 float:left 与 display:inline-block 的 jQuery UI 拖放排序比较

    我这里有两个例子 这两个例子之间的唯一区别是 一种使用display inline block 另一种使用float left li doc item 显示 内联块 与 li doc item float left 我的问题是 displa
  • jQuery:将文本区域滚动到给定位置

    我有一个包含很多文本的文本区域
  • AngularJS + jQuery 移动

    是否还有其他可能性来设计AngularJS以移动友好的方式应用程序CSS 我正在计划一个移动应用程序 并希望使用 AngularJS 进行逻辑和数据绑定 但我不想自己设计所有内容CSS The AngularJSFAQ说它使用jQuery
  • Svelte 条件元素类报告为语法错误

    我正在做一个if块每if 块的精简指南 https svelte technology guide if blocks 这看起来很简单 但 Svelte 认为这是一个语法错误 svelte plugin ParseError Unexpec
  • 为什么 call 比 apply 快那么多?

    我想知道是否有人知道why call比apply 在 Chrome 中 速度大约快 4 倍 在 Firefox 中快 30 倍 我什至可以制作自定义原型 apply2 在大多数情况下 运行速度是apply 这个想法取自角度 Function
  • 如何将MathJax公式转换为img

    Mathjax 现在在我的项目中运行良好 但有一个问题 有没有办法将MathJax的公式 纯html和css 转换成img文件 我可以保存 MathJax 可以配置为生成 SVG 看http docs mathjax org en late
  • 当php脚本通过ajax运行时显示进度条

    我有一个通过 ajax 向服务器提交值的表单

随机推荐

  • linux kernel file_open

    内核空间与用户空间 在vfs read和vfs write函数中 其参数buf指向的用户空间的内存地址 如果我们直接使用内核空间的指针 则会返回 EFALUT 这是因为使用的缓冲区超过了用户空间的地址范围 一般系统调用会要求你使用的缓冲区不
  • Qt VTK ITK安装与测试(三)ITK的安装与测试

    ITK的安装与测试 安装简介 本部分讲述使用cmake加VS2010编译 安装ITK库 基本步骤和VTK安装时相同 而后测试VTK与ITK的联合开发 VTK安装步骤 1 资源下载 资源下载网址 http www itk org ITK re
  • 【笔记】python中的for循环(遍历列表)、for循环中的一些缩进问题

    文章目录 一 for循环 遍历列表 在for循环中执行更多的操作 二 for循环中的一些缩进问题 一 for循环 遍历列表 我们经常需要遍历列表的所有元素 对每个元素执行相同的操作 这种情况下 我们就需要使用for循环 下面我们举几个栗子来
  • 专业CPU信息检测工具:CPU-Z

    今天小编为大家测试了一款轻量级的CPU处理器的测试工具 可以查看CPU的详细信息 以供各位同学们学习 一 简单介绍 CPU Z是一款非常流行的CPU检测软件 被广大用户所熟知 它是目前最受欢迎的CPU检测软件之一 除了Intel和AMD自带
  • SQLi-LABS Less-29到Less-31

    Less 29题目 Less 29的题目提到了WAF 看题目的意思应该是一个保护网站的东西 百度了一下 WAF就是Web Application Firewall 主要功能是拦截入侵尝试 比如SQL Injection XSS 路径遍历 窃
  • Java中insert()方法的使用

    insert 函数表示在字符串中插入字符串 StringBuffer insert int index String str 在Java中最常用instert方法的是这两个类型StringBuffer和StringBuilder 首先需要先
  • elementui的文件上传功能-.上传文件-带参数-手动

    elementui的文件上传功能 上传文件 带参数 手动
  • JS函数(二)基础 return 返回值

    创建函数 function 函数名 形参变量列表 函数体 return 返回值 return 1 什么是 返回 return语句将终止当前函数并返回当前函数的值 2 为什么要用 我们先来看一组代码
  • matlab矩阵(一)--如何控制矩阵中小数点的位数

    format 设置输出格式对浮点性变量 缺省为format short format并不影响matlab如何计算和存储变量的值 对浮点型变量的计算 即单精度或双精度 按合适的浮点精度进行 而不论变量是如何显示的 对整型变量采用整型数据 整型
  • 汤姆·克鲁斯 - 电影全集

    1 熄灯号 Taps 1981 导演 哈诺德 贝克编剧 Darryl Ponicsan Devery Freeman James Lineberger主演 乔治 C 斯科特 蒂莫西 赫顿 罗尼 考克斯 西恩 潘 汤姆 克鲁斯 伊万 汉德勒
  • HLS图像处理系列——在ZEDBoard搭建DDR图像处理通路

    ZYNQ芯片内包含一个丰富特性的基于双核ARM Cortex A9的处理子系统 Processing System PS 和Xilinx 28nm可编程逻辑 Programmable Logic PL PS除了核心外还包括片上存储器 外部存
  • 端口扫描程序nmap使用手册

    摘要 nmap是一个网络探测和安全扫描程序 系统管理者和个人可以使用这个软件扫描大型的网络 获取那台主机正在运行以及提供什么服务等信息 nmap支持很多扫描技术 例如 UDP TCP connect TCP SYN 半开扫描 ftp代理 b
  • [原创]Discuz!NT1.1高亮代码插件1.5稳定版!

    Discuz NT 1 1高亮代码插件FOR NET2 0 作者 winson 版本 1 5稳定版 支持网站 http bbs szblogs com 功能 为论坛增加高亮代码显示 类似cnblogs的形式 一 1 5版修复的问题 之前发布
  • cgi和fastcgi获取所有环境变量(C语言)

    参考 http blog chinaunix net uid 620765 id 2084386 html 还记得C语言main函数的完整声明吗 int main int argc char argv char envp 这个就是c语言中m
  • c语言解惑 ----读书笔记

    c语言解惑 前言 左值和右值 首地址 没有初始化指针和空指针 指针的初始化 小段存储和大端存储 数组 函数 文件结构 多文件结构 多文件中的函数设计 头文件 前言 面试过程中 发现自己确实对于c和c 实现的原理了解得不够深入 因此借书以解惑
  • IE和FF下查看HTTP头文件信息的插件

    通过这2个插件可以获得服务器返回的页面header信息 对于解析web服务器的工作情况非常有帮助 如查看gzip压缩是否成功及html文件的大小 iehttpheaders下载地址 http www blunck se iehttphead
  • 论文阅读Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks

    论文信息 题目 Sentence Embeddings using Siamese BERT Networks 使用孪生BERT网络结构的句子嵌入 作者 Nils Reimers Iryna Gurevych 论文水平 EMNLP 2019
  • 工程师必知的代码重构指南

    作者 CATE LAWRENCE 译者 冬雨 策划 蔡芳芳 本指南将带你了解进行代码重构的好处 可能遇到的挑战 可以采用的工具和最佳实践 以及重构和技术债务之间的区别 我们都在寻找清理代码 降低复杂性和改进功能的方法 重构就是其中之一 本指
  • mongoDB操作命令大全,掌握最常用的命令

    目录 添加操作 查询操作 复合主键 逻辑操作符匹配 文档游标 文档投影 更新操作 删除操作 添加操作 db 集合 insertOne
  • 面试题二:谈一谈对JavaScript中for in与for of的理解

    for in for in 语句以任意顺序迭代一个对象的除Symbol以外的可枚举属性 包括继承的可枚举属性 在没有深度了解过for in的作用前 我只停留在它能够遍历对象键和数组下标的的层面上 那现在就来探究什么是一个对象中的Symbol