02-JS八股文
码路教育 6/28/2022
# JS八股文(码路教育)
# 1. 说一下 JavaScript 中的有哪些数据类型
基本类型:
1)number
2)string
3)boolean
4)null
5)undefiend
6)symbol
引用类型:
1)数组
2)对象
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 2. i++和++i的区别
++在前,整体是新值
++在后,整体是旧值
i的值都要加1
1
2
3
4
2
3
4
# 3. 说出==和===的区别?
== (普通相等)
1)在类型不相同的情况下, 会将运算元先转成Number的值, 再进⾏⽐较(隐式转换)
2)null⽐较特殊: null在进⾏⽐较的时候, 应该是会被当成⼀个对象和原⽣类型进⾏⽐较的
=== (严格不等)
1)在类型不同的情况下,直接返回false
1
2
3
4
5
6
2
3
4
5
6
# 4. 什么是函数回调?什么匿名函数??
回调函数:
1)个函数作为另外⼀个函数的参数,称之为回调函数,也称为⾼阶函数
匿名函数:
1)如果在传⼊⼀个函数时,我们没有指定这个函数的名词或者通过函数表达式指定函数对应的变量,那么这个函数称之为匿名函数
1
2
3
4
5
2
3
4
5
# 5. attribute和Property的区别和关系
attribute:
1)浏览器解析HTML元素时 会将对应的属性(attribute)放在对应的元素对象上
2)具体分为标准的属性和⾮标准的属性
3)标准属性: id class href type value等等
4)⾮标准属性(⾃定义) :abc age height
Property:
1)对于标准的attribute 会在DOM对象上创建对应的property属性
2)⼤多数情况下 他们是相互作⽤的 改变其中⼀个 另⼀个也会随之改变
3)⼤多数情况 推荐获取attribute 使⽤property⽅式 因为它默认是有类型的
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 6. prefetch 与 preload 的区别是什么?
<link rel="prefetch" href="style.css" as="style" />
<link rel="preload" href="main.js" as="script" />
preload 是⼀个声明式 fetch,可以强制浏览器在不阻塞 document 的 onload 事件的情况下请求资源。 preload 顾名思义就是⼀种预加载的⽅式,它通过声明向浏览器声明⼀个需要提前加载的资源,当资源真正被使⽤的时候⽴即执⾏,就⽆需等待⽹络的消耗。
prefetch 告诉浏览器这个资源将来可能需要,但是什么时间加载这个资源是由浏览器来决定的。若能预测到⽤户的⾏为,⽐如懒加载,点击到其它⻚⾯等则相当于提前预加载了需要的资源。
1
2
3
4
5
6
2
3
4
5
6
# 7. preload和prefetch的应⽤场景?
webpack优化之preload和prefetch:
1)单⻚⾯应⽤由于⻚⾯过多,可能会导致代码体积过⼤,从⽽使得⾸⻚打开速度过慢。所以切分代码,优化⾸屏打开速度尤为重要。
2)但是所有的技术⼿段都不是完美的。当我们切割代码后,⾸屏的js⽂件体积减少了好多。但是也有⼀个突出的问题:
3)那就是当跳转其他⻚⾯的时候,需要下载相应⻚⾯的js⽂件,这就导致体验极其不好,每⼀次点击访问新⻚⾯都要等待js⽂件下载,然后再去请求接⼝获取数据。频繁出现loading动画的体验真的不好
4)所以如果我们在进⼊⾸⻚后,在浏览器的空闲时间提前下好⽤户可能会点击⻚⾯的js⽂件,这样⾸屏的js⽂件⼤⼩得到了控制,⽽且再点击新⻚⾯的时候,相关的js⽂件已经下载好了,就不再会出现loading动画。
动态引⼊js⽂件,实现code-splitting,减少⾸屏打开时间
1)按引⼊情况加载,只需添加注释即可
2)代码分割注释:/webpackChunkName: 'mp-supports'/
3)prefetch注释:/* webpackPrefetch: true */
4)更多的,可以查看 webpack 注释⿊魔法:https://webpack.js.org/api/module-methods/#magic-comments
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 8. DOM 中 Element 与 Node 有何区别
Element 继承于 Node,具有 Node 的⽅法,同时⼜拓展了很多⾃⼰的特有⽅法。
1
# 9. 点击 a 标签下载⽂件如何做?
有两种⽅式:
1)a.download 当指定 a 标签的 download 属性时,点击该链接会直接保存为⽂件,⽂件名为download 属性
2)通过对 a 标签指定的 URL 在服务器设置响应头 Content-Disposition: attachment;filename="filename.jpg" 可直接下载
1
2
3
2
3
# 10. this的绑定规则有⼏种?
默认绑定:独⽴函数调⽤,函数没有被绑定到某个对象上进⾏调⽤
隐式绑定:通过某个对象发起的函数调⽤,在调⽤对象内部有⼀个对函数的引⽤。
显式绑定:明确this指向的对象,第⼀个参数相同并要求传⼊⼀个对象。
new绑定:
1)创建⼀个全新对象
2)新对象被执⾏prototype链接
3)新对象绑定到函数调⽤的this
4)如果函数没有返回其他对象,表达式会返回这个对象
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 11. 如何实现⻚⾯⽂本不可复制
有 CSS 和 JS 两种⽅法,以下任选其⼀或结合使⽤
1)使⽤ CSS 如下:user-select: none;
2)使⽤ JS 如下,监听 selectstart 事件,禁⽌选中。当⽤户选中⼀⽚区域时,将触发 selectstart 事件,Selection API 将会选中⼀⽚区域。禁⽌选中区域即可实现⻚⾯⽂本不可复制。
document.body.onselectstart = (e) => { e.preventDefault(); };
document.body.oncopy = (e) => { e.preventDefault();};
1
2
3
4
5
6
2
3
4
5
6
# 12. 常⽤的数组操作⽅法有哪些?
Array.shift()
Array.pop()
Array.push(param1[,param2,...paramN])
Array.unshift(newElement1[,newElement2,...newElementN])
Array.join([separator])
Array.reverse()
1
2
3
4
5
6
2
3
4
5
6
# 13. 说出对DOM和document对象的理解
DOM:⽂档对象模型(Document Object Model)将⻚⾯所有的内容表示为可以修改的对象
1)浏览器将我们编写在HTML中的每⼀个元素(Element)都抽象成了⼀个个对象
2)所有这些对象都可以通过JavaScript来对其进⾏访问,那么我们就可以通过JavaScript来操作⻚⾯;
3)所以,我们将这个抽象过程称之为 ⽂档对象模型(Document Object Model)
Document节点表示的整个载⼊的⽹⻚,它的实例是全局的document对象:
1)对DOM的所有操作都是从 document 对象开始的
2)它是DOM的⼊⼝点,可以从document开始去访问任何节点元素;
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 14. for in和for of的区别?
在JavaScript中遍历数组通常是使⽤for...i循环,在ES5具有遍历数组功能的还有forEach、map、filter、some、every、reduce、reduceRight等。for...in和for...of是两种增强型循环,for...in是ES5标准,在ES6中新增了for...of的循环⽅式。
for...in :遍历以任意顺序迭代⼀个对象的除Symbol以外的可枚举属性,包括继承的可枚举属性。
for...of:遍历在可迭代对象,包括 Array , Map , Set , String , TypedArray ,arguments 对象等等
它们的区别:
1)for...in可以遍历对象和数组,for...of不能遍历对象
2)for...in 循环不仅遍历对象的键名,还会遍历⼿动添加的其它键,甚⾄包括原型链上的键
3)for...in遍历的索引为字符串类型
4)for..of适⽤遍历数/数组对象/字符串/map/set等拥有迭代器对象的集合,但是不能遍历对象
5)for...of与forEach()不同的是,它可以正确响应break、continue和return语句
6)具有迭代器对象才可以使⽤for...of
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 15. 什么是纯函数?如何编写纯函数?
纯函数:纯函数⼀般具有以下的特点:
1)确定的输⼊⼀定会有确定的输出(外部环境的任何变化不会影响函数内部的操作产⽣的结果)
2)纯函数的执⾏不会产⽣副作⽤。(函数内部的操作也不会对函数外部产⽣任何影响)
纯函数在react和redux中应⽤⽐较多。编写纯函数:
1
2
3
4
5
2
3
4
5
//⼀般的数学⽅法可以写成纯函数,例如相加
function sum(...args) {
var result = args.reduce((perValue, item) => {
return preValue + item
}, 0)
return result
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 16. 说说你对严格模式的理解?
严格模式是⼀种JavaScript的限制模式,因为种种历史原因,JavaScript语⾔在⾮严格模式下是⽐较松散的。在JavaScript不断优化和加⼊新特性的过程中,为了兼容早期的JavaScript,⼀些错误和不规范的写法也被保留了下来。这些错误也不会被抛出。在开启了严格模式后,js引擎会以⼀种更严格的规范执⾏JavaScript代码,⼀些不规范的写法和错误也会直接抛出。
的写法和错误也会直接抛出。
1)对⽂件开启:在⽂件的开头写上"use strict"
2)对函数开启:在函数的开头写上"use strict"
严格模式下的语法限制:
1)不允许意外创建全局变量(不写var、let、const这种声明变量的关键字)
2)会对静默失败的赋值操作抛出异常
3)试图删除不可删除的属性
4)不允许函数参数有相同的名称
5)不允许只有0开头的⼋进制语法
6)不允许使⽤with
7)⽆法获取eval中定义的变量
8)this绑定不会默认转成对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 17. 浏览器内核是什么?有哪些常⻅的浏览器内核?
浏览器内核⼜称浏览器渲染引擎,是浏览器的最核⼼部分。负责解析⽹⻚语法并渲染⽹⻚。
常⻅的浏览器内核有:
1)trident(三叉戟)---- IE浏览器、360安全浏览器、UC浏览器、搜狗⾼速浏览器、百度浏览器
2)gecko(壁⻁) ---- Mozilla、Firefox
3)pestro -> Blink ---- Opera
4)Webkit ---- Safari、360极速浏览器、搜狗⾼速浏览器、移动端浏览器
5)Webkit -> Blink ----Chrome、Edge
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 18. 说出浏览器输⼊⼀个URL到⻚⾯显示的过程?
URL 输⼊:
1)检查输⼊的内容是否是⼀个合法的 URL 链
2)判断输⼊的 URL 是否完整, 如果不完整,浏览器可能会对域进⾏猜测,补全前缀或者后缀
3)使⽤⽤户设置的默认搜索引擎来进⾏搜索
DNS 解析:
1)浏览器不能直接通过域名找到对应的服务器 IP 地址
2)所以需要进⾏ DNS 解析,查找到对应的 IP 地址进⾏访问。
建⽴ TCP 连接:
1)三次握手
发送 HTTP / HTTPS 请求(建⽴ TLS 连接):
1)向服务器 发起 TCP 连接请求
2)当这个请求到达服务端后,通过 TCP 三次握⼿,建⽴ TCP 的连接。
服务器响应请求:
1)当浏览器到 web 服务器的连接建⽴后,浏览器会发送⼀个初始的 HTTP GET 请求,请求⽬标通常是⼀个 HTML ⽂件。服务器收到请求后,将发回⼀个 HTTP 响应报⽂,内容包括相关响应头和 HTML 正⽂。
浏览器解析渲染⻚⾯:
1)处理 HTML 标记并构建 DOM 树。
2)处理 CSS 标记并构建 CSSOM 树。
3)将 DOM 与 CSSOM 合并成⼀个渲染树
4)根据渲染树来布局,以计算每个节点的⼏何信息。
5)将各个节点绘制到屏幕上。
HTTP 请求结束,断开 TCP 连接
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 19. 说说线程和进程的区别以及关系
进程:
1)是 cpu 分配资源的最⼩单位;(是能拥有资源和ᇿ⽴运⾏的最⼩单位)
2)计算机已经运⾏的程序,是操作系统管理程序的⼀种⽅式 (官⽅说法)
3)可以认为启动⼀个应⽤程序,就会默认启动⼀个进程(也可能是多个进程)(个⼈解释)
4)也可以说进程是线程的容器
线程:
1)是 cpu 调度的最⼩单位;(线程是建⽴在进程的基础上的⼀次程序运⾏单位,⼀个进程中可以有多个线程)
2)操作系统能够运⾏运算调度的最⼩单位,通常情况下它被包含在进程中 (官⽅说法)
3)每⼀个进程中,都会启动⾄少⼀个线程⽤来执⾏程序中的代码,这个线程被称之为主线程
操作系统如何做到同时让多个进程同时⼯作?
1)因为 CPU 的运算速度⾮常快, 可以快速的在多个进程之间迅速的切换
2)当进程中的线程获取到世间⽚时, 就可以快速执⾏我们编写的代码
3)由于 CPU 执⾏速度过于变态, 对于⽤户来说是感受不到这种快速切换的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 20. JavaScript JavaScript为什么是单线程?
1)这主要和js的⽤途有关,js是作为浏览器的脚本语⾔,主要是实现⽤户与浏览器的交互,以及操作dom
2)这决定了它只能是单线程,否则会带来很复杂的同步问题。
3)⽐如js被设计了多线程,如果有⼀个线程要修改⼀个dom元素,另⼀个线程要删除这个dom元素,此时浏览器就会⼀脸茫然,不知所措。
4)所以,为了避免复杂性,从⼀诞⽣,JavaScript就是单线程,这已经成了这⻔语⾔的核⼼特征,将来也不会改变
1
2
3
4
2
3
4
# 21. 常⻅的节点(Node)属性
nodeType:
1)获取节点的类型
2)⽐如 注释节点8 ⽂本节点3 元素节点1
tagName:
1)获取元素的标签名词 仅适⽤于Element节点
nodeName:
1)获取元素的标签名词 适⽤于任何Node节点
innerHTML,textContent:
1)前者将元素中的HTML获取为字符串属性 后者仅仅获取⽂本内容
outerHTML:
1)包含了完整的HTML
2)相当于innerHTML加上元素本身
nodeValue/data:
1)获取⾮元素节点的⽂本内容
hidden:
1)⽤于设置元素隐藏(全局属性)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 22. 说说load和DOMContentLoaded的区别
load:
1)浏览器加载完所有的HTML 还加载完所有的外部资源 样式 图⽚等
DOMContentLoaded:
1)HTML⽂档所有资源都加载完成 并构建了DOM树 但是⼀些外部资源还没有加载完成 如图⽚的src
1
2
3
4
5
2
3
4
5
# 23. 浏览器是多进程的?
1)在浏览器中,每打开⼀个tab⻚⾯,其实就是新开了⼀个进程,在这个进程中,还有ui渲染线程,js引擎线程,http请求线程等。
2)因此浏览器是⼀个多进程的。为了利⽤多核CPU的计算能⼒,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是⼦线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。
1
2
2
# 24. 什么是重排重绘,如何减少重排重绘
重排(Reflow):
1)元素的位置发⽣变动时发⽣重排,也叫回流。此时在关键渲染路径中的 Layout 阶段,计算每⼀个元素在设备视⼝内的确切位置和⼤⼩。当⼀个元素位置发⽣变化时,其⽗元素及其后边的元素位置都可能发⽣变化,代价极⾼。
重绘(Repaint):
1)元素的样式发⽣变动,但是位置没有改变。此时在关键渲染路径中的 Paint 阶段,将渲染树中的每个节点转换成屏幕上的实际像素,这⼀步通常称为绘制或栅格化
另外,重排必定会造成重绘。以下是避免过多重排重绘的⽅法:
1)使⽤ DocumentFragment 进⾏ DOM 操作,不过现在原⽣操作很少也基本上⽤不到
2)CSS 样式尽量批量修改
3)避免使⽤ table 布局
4)为元素提前设置好⾼宽,不因多次渲染改变位置
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 25. JSON的相关⽤法和应⽤场景?
三种⽤法:
1)简单值: 数字 字符串 布尔类型
2)对象值 key value组成 必须添加双引号 value 可以是简单值 对象值 数组值
3)数组值 内容可以是对象值 简单值 数组值
应⽤场景:
1)⽹络传输的JSON数据
2)项⽬的某些配置⽂件
3)⾮关系型数据库将JSON作为存储⽂件
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 26. 说说ES6~ES13新增了哪些知识点?
课件中有讲ES6~ES13,自己总结。
1
# 27. 说说事件冒泡和事件捕获的理解?
事件冒泡:
1)从最内层的元素向外依次传递的顺序, 默认是事件冒泡
事件捕获:
1)从外层到内层依次传递的顺序 ,可以通过addEventListener("click",fn,true) 监听事件捕获
1
2
3
4
5
2
3
4
5
# 28. 说说什么是Reflect和为什么需要使⽤它
什么是Reflect:
1)Reflect是⼀个对象,提供了多种⽅法⽅便我们统⼀管理对象。
2)Reflect 是⼀个内置的对象,它提供拦截 JavaScript 操作的⽅法。这些⽅法与proxy handlers (enUS)的⽅法相同。
3)与⼤多数全局对象不同 Reflect 并⾮⼀个构造函数,所以不能通过new 运算符对其进⾏调⽤,或者将 Reflect 对象作为⼀个函数来调⽤。
4)Reflect 的所有属性和⽅法都是静态的(就像 Math 对象)。
为什么需要使⽤Reflect:
1)在对对象进⾏操作时有些⽅法会有返回值,操作对象变的更加规范
2)Object作为构造函数,操作对象的⽅法放在它身上不是很合适,早期的设计不规范导致的。
3)在使⽤Proxy监听对象时,⽤Reflect来操作对象避免了对原对象的直接操作。
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 29. 介绍下 Set、Map的区别?
Set
成员不能重复
只有键值没有键名,类似数组
可以遍历,方法有add, delete,has
Map
本质上是健值对的集合,类似集合
可以遍历,可以跟各种数据格式转换
应用场景:Set用于数据重组,Map用于数据储存
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 30. 你对事件委托的理解?
利⽤事件的冒泡机制,以及事件对象中可以准确获知触发事件的元素机制(e.target),将⼦元素事件委托给⽗元素处理的现象。
1
# 31. 什么是事件委托?
事件委托,就是利用了事件冒泡的机制,在较上层位置的元素上添加一个事件监听函数,
来管理该元素及其所有子孙元素上的某一类的所有事件。
适用场景:在绑定大量事件的时候,可以选择事件委托
事件委托可以减少事件注册数量,节省内存占⽤!
当新增⼦元素时,⽆需再次做事件绑定,因此非常适合动态添加元素 (vue解析模板时, 会对新创建的元素, 额外进行绑定的)
1
2
3
4
5
6
7
2
3
4
5
6
7
# 32. 项目开发过程中,都使用到了哪些ES6新特性?
1, 项目中使用let或const
2, 解构赋值,数组解构赋值,对象解构赋值,函数参数解构赋值,减少代码量
3,展开运算符
4,rest运算符
5,箭头函数
6,对象的中属性的简写,方法的简写
7,Object.assign( )方法
8,Symbol
9,Proxy vue3中数据响应式的核心
10,Set和Map集合
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 32. 实现(a == 1 && a == 2 && a == 3)为true
<script>
// a等于什么值,会使条件成⽴「两种⽅案」
// var a = "?";
// valueOf方法
// 解法一
// let a = {
// i:0,
// valueOf(){
// return ++this.i
// }
// }
// 解法二
// let a = {
// i:0,
// toString(){
// return ++this.i
// }
// }
// 解法三
// let a = [1,2,3];
// a.toString = a.shift;
// 解法四
let i = 0;
Object.defineProperty(window, "a", {
get() {
// console.log("get...");
return ++i;
}
})
if (a == 1 && a == 2 && a == 3) {
console.log('码路教育');
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 33. 如何给数组降维
<script>
// 利用Array.some方法判断数组中是否还存在数组,es6展开运算符连接数组
let arr = [1, 2, [3, 4, [5]]]
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr)
}
console.log(arr);
</script>
<script>
// 使用数组的concat方法
let arr = [1, 2, [3, 4]]
let result = []
// result = Array.prototype.concat.apply([], arr)
result = [].concat(...arr)
console.log(result);
</script>
<script>
// es6中的flat函数也可以实现数组的扁平化
let arr = [1, 2, ['a', 'b', ['中', '⽂', [1, 2, 3, [11, 21, 31]]]], 3];
let result = arr.flat(Infinity)
console.log(result);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 34. 一个关于闭包的经典面试题
<script>
// 下面代码是否可以,每隔1000MS依次输出 0 1 2 3 4 5 ?
// 如果不可以,说明为啥?以及如何解决?
// for循环是同步代码 定时器是异步代码
// 同步代码执行完毕后,i的值已经变了6
// for (var i = 0; i < 6; i++) {
// setTimeout(function () {
// console.log(i);
// }, (i + 1) * 1000);
// }
// 利用闭包解决
// for (var i = 0; i < 6; i++) {
// (function (i) {
// setTimeout(function () {
// console.log(i);
// }, (i + 1) * 1000);
// })(i)
// }
// 利用块级作用域
// let + {} 会形成块级作用域
// for (let i = 0; i < 6; i++) {
// setTimeout(function () {
// console.log(i);
// }, (i + 1) * 1000);
// }
// 利用闭包解决
for (let i = 0; i < 6; i++) {
setTimeout((function(i) {
return function() {
console.log(i);
}
})(i), i * 1000)
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 35. 检测一个对象是否是纯对象
<script>
// 检测一个对象是否是纯对象
const isPlainObject = function isPlainObject(obj) {
let prototype;
return Object.prototype.toString.call(obj) === '[object Object]' &&
(prototype = Object.getPrototypeOf(obj), prototype === null ||
prototype == Object.getPrototypeOf({})
)
};
// {} new Object() Object.create(null)
console.log(isPlainObject({}));
console.log(isPlainObject(new Object()));
console.log(isPlainObject(Object.create(null)));
console.log("-------------");
function Person() {}
let p = new Person()
console.log(isPlainObject(new Array()));
console.log(isPlainObject(p));
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 36. 手写JSONP实现原理
前端代码
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
<button id="btn">发送ajax请求</button>
<script>
let btn = document.getElementById("btn");
function jsonp(options) {
let callBackName = "wangcai";
window[callBackName] = function(data) {
if (data != null) {
options.success(data)
} else {
options.fail()
}
}
let url = options.url + "?callBack=" + callBackName
let scriptEle = document.createElement("script");
scriptEle.src = url;
document.body.append(scriptEle)
}
btn.onclick = function() {
jsonp({
url: "http://localhost:3000/",
success: function(data) {
console.log("data:", data);
},
fail: function(err) {
console.log("数据请求失败了");
}
})
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
后端代码
const Koa = require("koa");
const cors = require("koa2-cors");
const logger = require("koa-logger");
const Router = require("@koa/router");
const koaBody = require("koa-body");
const app = new Koa();
const router = new Router();
app.use(cors());
app.use(logger());
app.use(koaBody());
router.get("/", (ctx) => {
let cb = ctx.query.callBack;
// console.log(cb);
// 后端返回函数调用字符串
ctx.body = `${cb}(${JSON.stringify({ a: 1, b: 2 })})`
})
app.use(router.routes())
router.allowedMethods();
app.listen(3000, () => {
console.log("running in http://127.0.0.1:3000");
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 37. Vue2数据响应式原理
<script>
// vue2.x的响应式原理
function updateView() {
console.log("视图更新了~");
}
function definedReactive(target, key, value) {
// console.log(target,key,value);
observer(value); // 深度监听
Object.defineProperty(target, key, {
get() {
// console.log("get...");
return value
},
set(newValue) {
observer(newValue); // 深度监听
value = newValue
// 数据变了,我监听到了,我要去更新视图
updateView()
}
})
}
function observer(target) {
if (typeof target !== "object" || target === null) {
return target
}
for (let key in target) {
definedReactive(target, key, target[key])
}
}
// 准备数据
// 目的:name,age,info,address
let data = {
name: "码路",
age: 18,
info: {
address: "bj",
}
};
// 监听数据,数据劫持
observer(data)
// 当数据变了,要更新视图
// console.log(data.name);
// data.name = "malu";
// console.log(data.name);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 38. Vue2数组响应式原理
<script>
// vue2.x的响应式原理
function updateView() {
console.log("视图更新了~");
}
// 把Array的原型对象,赋值给oldArrayProperty
let oldArrayProperty = Array.prototype;
let arrProto = Object.create(oldArrayProperty);
["push", "pop", "shift", "unshift", "splice"].forEach(methodName => {
// console.log(methodName);
// 调用这7个方法时,调用的并不是原生的push是我们自己重写后的push
arrProto[methodName] = function() {
updateView(); // 更新视图
oldArrayProperty[methodName].call(this, ...arguments)
}
})
function definedReactive(target, key, value) {
// console.log(target,key,value);
observer(value); // 深度监听
Object.defineProperty(target, key, {
get() {
// console.log("get...");
return value
},
set(newValue) {
observer(newValue); // 深度监听
value = newValue
// 数据变了,我监听到了,我要去更新视图
updateView()
}
})
}
function observer(target) {
if (typeof target !== "object" || target === null) {
return target
}
if (Array.isArray(target)) {
// console.log(target+"是数组");
// 如果是数组,改变原型对象
target.__proto__ = arrProto;
}
for (let key in target) {
definedReactive(target, key, target[key])
}
}
// 准备数据
// 目的:name,age,info,address
let data = {
name: "码路",
age: 18,
info: {
address: "bj",
},
arr: ["ml", "wc", "xq"]
};
// data.arr.push("z3") 当调用push时,模板也需要重新渲染
// 在vue2.x中重写了数组的7个方法,在7个方法中更新了视图
// 监听数据,数据劫持
observer(data)
// 当数据变了,要更新视图
// console.log(data.name);
// data.name = "malu";
// console.log(data.name);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# 39. 防抖实现
<!--
说说你对防抖、 节流的理解, 以及它们的区别和应用场景?
防抖: 将多次执行函数变成最后⼀ 次执行 等待固定时间还没有事件触发时执行的函数
应用场景:
按钮的点击
屏幕滚动时的复杂计算
输⼊ 框输⼊ 时进行搜索
用户缩放浏览器的resize事件
简单的防抖函数实现:
-->
<script>
function myDebounce(execFn, delay) {
let timer = 0
function _debounce(...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
execFn.apply(this, args)
timer = null
}, delay)
}
return _debounce
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 40. 节流实现
<!--
节流: 按照固定的时间频率(间隔)来执⾏对应的函数
应用场景:
监听页面的滚动事件 通过节流来降低事件调用的频率
⿏标移动
用户频繁点击按钮的操作
简单实现:
-->
<script>
function myThrottle(execFn, interval) {
let initTime = 0
function throttle(...args) {
let nowTime = Date.now()
const waitTime = interval - (nowTime - initTime)
if (waitTime <= 0) {
execFn.apply(this, args)
initTime = nowTime
}
}
return throttle
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 41. 数组去重
<script>
// 利用ES6 Set去重(ES6中最常用)
function unique(arr) {
return Array.from(new Set(arr))
}
var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];
console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {}]
</script>
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
<script>
// 利用for嵌套for,然后splice去重(ES5中最常用)
function unique(arr) {
for (var i = 0; i < arr.length; i++) {
for (var j = i + 1; j < arr.length; j++) {
if (arr[i] == arr[j]) { //第⼀个等同于第二个,splice方法删除第二个
arr.splice(j, 1);
j--;
}
}
}
return arr;
}
var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];
console.log(unique(arr))
// [1, "true", 15, false, undefined, NaN, NaN, "NaN", "a", {…}, {…}] //NaN和{}没有去重,两个null直接消失了
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>
// 利用indexOf去重
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return
}
var array = [];
for (var i = 0; i < arr.length; i++) {
if (array.indexOf(arr[i]) === -1) {
array.push(arr[i])
}
}
return array;
}
var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];
console.log(unique(arr))
// [1, "true", true, 15, false, undefined, null, NaN, NaN, "NaN", 0, "a", {…}, {… }] //NaN、{}没有去重
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
// 利用sort去重
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return;
}
arr = arr.sort()
var arrry = [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (arr[i] !== arr[i - 1]) {
arrry.push(arr[i]);
}
}
return arrry;
}
var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];
console.log(unique(arr))
// [0, 1, 15, "NaN", NaN, NaN, {…}, {…}, "a", false, null, true, "true", undefined] // NaN、{}没有去重
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script>
// 利用includes
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return
}
var array = [];
for (var i = 0; i < arr.length; i++) {
if (!array.includes(arr[i])) { //includes 检测数组是否有某个值
array.push(arr[i]);
}
}
return array
}
var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];
console.log(unique(arr))
// [1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}]
// {} 没有去重
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script>
// 利用filter
function unique(arr) {
return arr.filter(function(item, index, arr) {
//当前元素,在原始数组中的第⼀个索引==当前索引值,否则返回当前元素
return arr.indexOf(item, 0) === index;
});
}
var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];
console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, "NaN", 0, "a", {…}, {…}]
</script>
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
<script>
// 利用递归去重
function unique(arr) {
return arr.reduce((prev, cur) => prev.includes(cur) ? prev : [...prev, cur], []);
}
var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];
console.log(unique(arr));
// [1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}]
</script>
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 42. JS继承实现方案
- 参考:http://47.94.210.129/malulesson/basic/jsplus/06.html
# 43. 实现柯里化函数
<script>
function mlcurrying(fn, ...args1) {
let length = fn.length;
let allArgs = [...args1];
let res = (...args2) => {
allArgs = [...allArgs, ...args2]
if (allArgs.length == length) {
return fn(...allArgs)
} else {
return res;
}
}
return res;
}
// 测试:
const add = (a, b, c) => a + b + c;
const a = mlcurrying(add, 1);
console.log(a(2, 3)) // 6
const b = mlcurrying(add, 1, 2, 3);
console.log(b()) // 6
const c = mlcurrying(add);
console.log(c(1, 2, 3)) // 6
const d = mlcurrying(add, 1)
console.log(d(2)(3)); // 6
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 44. 使用setTimeout实现setInterval
<script>
// 使用setTimeout实现setInterval
function mlSetTimout(fn, delay) {
let timer = null;
let interval = () => {
fn();
timer = setTimeout(interval, delay)
}
setTimeout(interval, delay)
return {
cancel() {
clearTimeout(timer)
}
}
}
// mlSetTimout(() => console.log(888), 1000);
let {
cancel
} = mlSetTimout(() => console.log(888), 1000);
setTimeout(() => {
cancel();
}, 5000);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 45. 简单实现hash路由
<html>
<style>
html,
body {
margin: 0;
height: 100%;
}
ul {
list-style: none;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
}
.box {
width: 100%;
height: 100%;
background-color: red;
}
</style>
<body>
<ul>
<li>
<a href="#red">红色</a>
</li>
<li>
<a href="#green">绿色</a>
</li>
<li>
<a href="#purple">紫色</a>
</li>
</ul>
<div class="box"></div>
<!-- <script>
let box = document.getElementsByClassName("box")[0];
window.onhashchange = function () {
let color = location.hash.slice(1);
box.style.background = color
}
</script> -->
<script>
// 封装成一个类
let box = document.getElementsByClassName("box")[0];
class HashRouter {
constructor(hashStr, cb) {
this.hashStr = hashStr;
this.cb = cb;
this.watchHash()
window.addEventListener("hashchange", this.watchHash.bind(this))
}
watchHash() {
let hash = location.hash.slice(1)
this.hashStr = hash;
this.cb(this.hashStr)
}
}
new HashRouter("red", (color) => {
box.style.background = color;
});
</script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# 46. 手写Promise.all的实现
<script>
// 类方法(静态方法) all
// 创建三个Promise
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve("p1 resolve")
reject("p1 reject error")
}, 3000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve("p2 resolve")
reject("p2 reject error")
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p3 resolve")
}, 5000)
})
// const p4 = 110;
Promise.all = function(promises) {
return new Promise((resolve, reject) => {
let result = [];
let index = 0;
let len = promises.length;
if (len === 0) {
resolve(result)
return;
}
for (let i = 0; i < len; i++) {
// promises[i]
Promise.resolve(promises[i]).then(data => {
result[i] = data;
index++;
if (index === len) {
resolve(result)
}
}).catch(err => {
reject(err)
})
}
})
}
// 类方法(静态方法) all
// all的作用:所有promise都成功后,得到所有成功后的promise结果
// 如果有一个先失败了,直接得到最先失败promise的结果
Promise.all([p1, p2, p3]).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
})
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# 47. 手写Promise.allSettled的实现
<script>
// 类方法(静态方法) allSettled
// 创建三个Promise
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p1 resolve")
// reject("p1 reject error")
}, 3000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p2 resolve")
// reject("p2 reject error")
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p3 resolve")
}, 5000)
})
function isPromise(val) {
return typeof val.then === "function"
}
Promise.allSettled = function(promises) {
return new Promise((resolve, reject) => {
let arr = [];
let times = 0;
let setData = (index, data) => {
arr[index] = data;
if (++times === promises.length) {
resolve(arr)
}
}
for (let i = 0; i < promises.length; i++) {
let current = promises[i];
if (isPromise(current)) {
current.then(data => {
setData(i, {
status: 'fulfilled',
value: data
})
}, err => {
setData(i, {
status: 'rejected',
value: err
})
})
} else {
setData(i, {
status: 'rejected',
value: current
})
}
}
})
}
// allSettled 获取所有的promise的结果,不管成功还是失败
Promise.allSettled([p1, p2, p3]).then(res => {
console.log("all settled:", res)
})
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# 48. 手写Promise.race的实现
<script>
// 创建三个Promise
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve("p1 resolve")
reject("p1 reject error")
}, 3000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve("p2 resolve")
reject("p2 reject error")
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p3 resolve")
}, 5000)
})
Promise.race = function(promises) {
return new Promise((resolve, reject) => {
let len = promises.length;
if (len === 0) return;
for (let i = 0; i < len; i++) {
Promise.resolve(promises[i]).then(data => {
resolve(data)
return;
}).catch(err => {
reject(err);
return;
})
}
})
}
// 类方法: race方法 race是比赛的意思
// 特点: 会等到第一个Promise有结果(无论这个结果是fulfilled还是rejected)
Promise.race([p1, p2, p3]).then(res => {
console.log("race promise:", res)
}).catch(err => {
console.log("race promise err:", err)
})
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 49. 实现 (5).add(3).minus(2)
<script>
// 实现 (5).add(3).minus(2) 功能
Number.prototype.add = function(n) {
return this.valueOf() + n;
}
Number.prototype.minus = function(n) {
return this.valueOf() - n;
}
// console.log((5).add(3));
console.log((5).add(3).minus(2));
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 50. alert(add(1)(2)(3)(4)(5))
<script>
// 实现 alert(add(1)(2)(3)(4)(5))
function add(x) {
let sum = x; // sum是存储和
let tmp = function(y) {
sum = sum + y;
return tmp;
}
tmp.toString = function() {
return sum;
}
return tmp;
}
// 当alert时,会调用toString方法
alert(add(1)(2)(3)(4)(5))
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 51. 工具函数之封装判断数据类型的函数
<script>
// 工具函数之封装判断数据类型的函数
// Object.prototype.toString()
function getType(data) {
if (data === null) return String(data)
return typeof data === "object" ? Object.prototype.toString.call(data).replace('[object ', '').replace(']', '').toLowerCase() : typeof data
}
// 调用
console.log(getType(null)); // -> null
console.log(getType(undefined)); // -> undefined
console.log(getType({})); // -> object
console.log(getType([])); // -> array
console.log(getType(123)); // -> number
console.log(getType(true)); // -> boolean
console.log(getType('码路')); // -> string
console.log(getType(/123/)); // -> regexp
console.log(getType(new Date())); // -> date
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 52. 实现深度比较isEqual
<script>
// 是不是对象
function isObject(obj) {
return typeof obj === "object" && obj !== null;
}
// 深度比较
function isEqual(obj1, obj2) {
if (!isObject(obj1) || !isObject(obj2)) {
// 说明其中有一个数据是基本数据类型
return obj1 === obj2
}
// 两个都是对象或数组,比较key的个数是否相同
let obj1Keys = Object.keys(obj1)
let obj2Keys = Object.keys(obj2)
if (obj1Keys.length !== obj2Keys.length) {
return false;
}
// 两个对象或数组中的key的个数是一样的
// 以其中一个为基准,遍历,递归比较
for (let key in obj1) {
// console.log(obj1[key]);
let res = isEqual(obj1[key], obj2[key])
if (!res) {
return false;
}
}
// 全相等
return true;
}
let obj1 = {
a: 1,
b: 2,
c: {
d: "d",
e: 'e'
}
}
let obj2 = {
a: 1,
b: 2,
c: {
d: "d",
e: 'e'
}
}
console.log(isEqual(obj1, obj2)); // true
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 53. 数组并集,交集,差集
<script>
// 并集
// [1,2,3] 并 [4,5,6] ==> [1,2,3,4,5,6]
// [1,2,3,3] 并 [4,5,6,6] ==> [1,2,3,4,5,6]
// [1,2,3,3] 并 [3,6,6] ==> [1,2,3,6]
let arr1 = [1, 2, 3, 3];
let arr2 = [4, 5, 6, 3, 2, 4, 9];
function union(arr1, arr2) {
let res = new Set([...arr1, ...arr2])
return [...res]
}
let newArr = union(arr1, arr2);
console.log(newArr)
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
// 交集
// [1,2,3] 交 [3,4,5,6] ==> [3]
// [1,2,3] 交 [7,8,9] ==> []
// [1,2,3] 交 [1,2,3] ==> [1,2,3]
let arr1 = [1, 2, 3, 3];
let arr2 = [3, 4, 5, 5];
function intersection(arr1, arr2) {
let s1 = new Set(arr1)
let s2 = new Set(arr2)
let newArr = [...s1].filter(item => {
return s2.has(item)
})
return newArr
}
let newArr = intersection(arr1, arr2);
console.log(newArr)
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
// 差集
// [1,2,3] 差 [3,4,5] ==> [1,2]
// [3,4,5] 差 [1,2,3] ==> [4,5]
let arr1 = [1, 2, 3];
let arr2 = [3, 4, 5];
function except(arr1, arr2) {
let s1 = new Set(arr1)
let s2 = new Set(arr2)
let newArr = [...s1].filter(item => {
return !s2.has(item)
})
return newArr;
}
console.log(except(arr1, arr2)) // [1,2]
console.log(except(arr2, arr1)) // [4,5]
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 54. 实现map方法
<script>
// 实现数组的map方法
Array.prototype.mlmap = function(fn) {
let newArr = [];
// this arr打点调用mlmap方法,mlmap方法中this表示arr
for (let i = 0; i < this.length; i++) {
let res = fn(this[i], i, this)
newArr.push(res)
}
return newArr;
};
var arr = [1, 3, 6];
// map可以对数组中每一个元素进行加工
// map返回加工后的新数组
var result = arr.mlmap(function(element, index, array) {
console.log("index", index);
console.log("array", array);
return element * element;
});
console.log("result: ===", result);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 55. 实现数组find方法
<script>
// 实现数组find方法
Array.prototype.mlfind = function(fn) {
// this是arr
for (let i = 0; i < this.length; i++) {
let res = fn(this[i])
if (res) {
// 表示元素满足条件
return this[i];
}
}
};
var arr = [1, 3, 6, 90, 23];
// find:查找满足条件的第1个元素
// item表示数组中每一个元素
var result = arr.mlfind(function(item) {
// 指定查找条件
return item > 6;
});
console.log(result);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 56. 实现filter函数
<script>
//模拟实现filter函数
Array.prototype.mlfilter = function(fn) {
let newArr = [];
// this表示array
for (let i = 0; i < this.length; i++) {
let res = fn(this[i])
if (res) {
newArr.push(this[i])
}
}
return newArr;
};
var array = [65, 56, 89, 53];
// filter是用来过滤数组中符合某个条件的元素
var arr = array.mlfilter(function(item) {
return item >= 60;
});
console.log("arr=>", arr);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 57. 实现some方法
<script>
//实现some方法
Array.prototype.mlsome = function(fn) {
// this 表示array
for (let i = 0; i < this.length; i++) {
let res = fn(this[i])
if (res) {
return res;
}
}
return false
};
let array = [1, 3, 5, 7, 90];
// 只要数组中一个元素满足条件,结果就true
let result = array.mlsome(function(item) {
return item > 10;
});
console.log("result=>", result);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 58. 实现every方法
<script>
//手动模拟实现every方法
Array.prototype.mlevery = function(fn) {
let flag = true;
// this
for (let i = 0; i < this.length; i++) {
let res = fn(this[i])
if (!res) {
// 有一个元素不满足条件
return false;
}
}
return flag
};
let array = [11, 31, 5, 71, 90];
// 如果数组中每一个元素都满足条件,结果就是true
// 只要有一个不满足结果就是false
let result = array.mlevery(function(item) {
return item > 10;
});
console.log("result=", result);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 59. 实现findIndex方法
<script>
// findIndex的实现
Array.prototype.mlfindIndex = function(fn) {
// this 表示arr
for (let i = 0; i < this.length; i++) {
if (fn(this[i], i)) {
return i
}
}
}
let arr = [1, 3, 4, 5, 7, 8];
// 找到满足条件的第1个元素的索引
let res = arr.mlfindIndex((item, index) => {
return item % 2 === 1
})
console.log(res)
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 60. 实现reduce方法
<script>
// 实现reduce方法
Array.prototype.mlreduce = function(fn, initVal) {
// 给prev传递了初始化参数,也就是说有初始化参数
let hasInitVal = initVal !== undefined
let value = hasInitVal ? initVal : this[0]
for (let i = hasInitVal ? 0 : 1, len = this.length; i < len; i++) {
value = fn(value, this[i], i, this)
}
return value;
};
var arr = [1, 2, 3];
// reduce是个累加器
// 第1次:prev:1 next:2
// 第2次:prev:3 next:3
// 第3次:prev:6 next:无
// prev表示之前累加和 next表示下一项
// 在调用reduce时可以给prev传递初始化参数
var sum = arr.mlreduce(function(prev, next, index, arr) {
// console.log("prev", prev);
// console.log("next", next);
return prev + next;
}, 10);
console.log(sum);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 61. 实现concat方法
<script>
// 实现concat方法
Array.prototype.mlconcat = function myconcat(...values) {
let newArr = [];
newArr.push(...this)
if (values.length === 0) {
return newArr
}
values.forEach(value => {
if (Array.isArray(value)) {
newArr.push(...value)
} else {
newArr.push(value)
}
})
return newArr;
}
let arr1 = [1, 2];
let arr2 = [3, 4];
let arr3 = [5, 6];
let arr4 = [7, 8];
// concat拼接多个数组形成一个数组
// 拼接也可以是一个普通值 返回一个新的数组
console.log(arr1.mlconcat(arr2, arr3, arr4, "hello"))
console.log(arr1.mlconcat("malu"))
console.log(arr1.mlconcat())
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 62. 实现slice方法
<script>
// 实现slice方法
Array.prototype.mlslice = function(start, end) {
let newArr = [];
// this表示arr1
// 如果要截取的数组是一个空数组,什么也不做,直接返回空数组
if (this.length === 0) {
return newArr;
}
// 处理start特殊情况
start = start || 0
if (start < 0) {
start = this.length + start
} else if (start >= this.length) {
return newArr;
}
// 处理end的特殊的情况
end = end || this.length;
if (end > this.length) {
end = this.length;
} else if (end <= start) {
end = this.length + end;
}
for (let i = start; i < end; i++) {
newArr.push(this[i])
}
return newArr;
}
// slice() 方法可从已有的数组中返回选定的元素。
// 返回一个新的数组,包含从 start(包括该元素) 到 end (不包括该元素)的 arrayObject 中的元素。
// start:规定从何处开始选取。如果该参数为负数,
// 则表示从原数组中的倒数第几个元素开始提取,
// slice(-2) 表示提取原数组中的倒数第二个元素到最后一个元素(包含最后一个元素)
// end:规定从何处结束选取。该参数是数组片断结束处的数组下标。
// 如果没有指定该参数,那么切分的数组包含从 start 到数组结束的所有元素。
// 如果该参数为负数, 则它表示在原数组中的倒数第几个元素结束抽取。
// slice(-2,-1) 表示抽取了原数组中的倒数第二个元素到最后一个元素(不包含最后一个元素,也就是只有倒数第二个元素)
let arr1 = [1, 3, 4, 5, 7, 9];
let res1 = arr1.mlslice(2, 4); // 2 4 代表都是索引
console.log(res1); // [4, 5]
let res2 = arr1.mlslice(-2, -1);
console.log(res2); // [7]
let res3 = arr1.mlslice();
console.log(res3) // [1, 3, 4, 5, 7, 9]
let res4 = arr1.mlslice(2);
console.log(res4) // [1, 3, 4, 5, 7, 9]
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# 63. 实现发布订阅
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button class="btn">点我</button>
<script>
// 实现发布订阅 事件总线
// 订阅 发布
class MyEventBus {
constructor() {
// {"malu":[f1,f2]}
this.eventMap = {}
}
on(eventName, eventFn) {
let eventFns = this.eventMap[eventName]
if (!eventFns) {
eventFns = [];
this.eventMap[eventName] = eventFns
}
eventFns.push(eventFn)
}
off(eventName, eventFn) {
let eventFns = this.eventMap[eventName];
if (!eventFns) return;
// {eat:[f1,f2,f3...]}
for (let i = 0; i < eventFns.length; i++) {
let fn = eventFns[i];
if (fn === eventFn) {
eventFns.splice(i, 1)
break;
}
}
// 如果eventFns已经清空了
if (eventFns.length === 0) {
delete this.eventMap[eventName]
}
}
emit(eventName, ...args) {
let eventFns = this.eventMap[eventName];
if (!eventFns) return;
eventFns.forEach(fn => {
fn(...args)
})
}
}
// 使用过程
const eventBus = new MyEventBus()
// 实现订阅
eventBus.on("malu", (name, age, height) => {
console.log("malu事件监听器01:", name, age, height);
})
let eat = (name, age, height) => {
console.log("eat事件监听器02", name, age, height);
};
eventBus.on("eat", eat)
setTimeout(() => {
eventBus.off("eat", eat)
}, 3000)
const btn = document.querySelector(".btn")
btn.onclick = function() {
// 发布
// eventBus.emit("malu", "wc", 18, 1.88)
eventBus.emit("eat", "wc", 18, 1.88)
}
</script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# 64. 实现Promise的resolve
<script>
// 实现 resolve 静态方法有三个要点:
// 传参为一个 Promise, 则直接返回它。
// 传参为一个 thenable 对象,返回的 Promise 会跟随这个对象,
// 采用它的最终状态作为自己的状态。
// 其他情况,直接返回以该值为成功状态的promise对象。
// 实现Promise中resolve静态方法
Promise.resolve = (param) => {
if (param instanceof Promise) return param
return new Promise((resolve, reject) => {
if (param && param.then && typeof param.then === "function") {
// console.log("传递的是一个thenable");
param.then(resolve, reject)
} else {
resolve(param)
}
})
}
const p1 = new Promise((resolve, reject) => {
resolve("p1 resolve")
// reject("p1 reject error")
})
// Promise.resolve("码路").then(res => {
// Promise.resolve(p1).then(res => {
Promise.resolve({
then(resolve, reject) {
resolve("p1 resolve")
}
}).then(res => {
console.log("then结果:", res)
})
// 相当于
// new Promise((resolve) => {
// resolve("hello")
// })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 65. 迭代器
<script>
// 什么是迭代器
const names = ["码路", "漫漫", "前端"];
// 给数组names创建一个迭代器(迭代器: names的迭代器)
let index = 0
// 迭代器就是一个对象,要求这个对象必须有一个next方法
// Iterator 是迭代器 就可以把一个容器中的元素迭代出来
const namesIterator = {
next() {
if (index < names.length) {
return {
done: false,
value: names[index++]
}
} else {
return {
done: true,
value: undefined
}
}
}
};
// 调用next返回一个对象,done表示是否迭代完毕,value表示迭代出来的数据
console.log(namesIterator.next()); // { done:false,value:"码路" }
console.log(namesIterator.next()); // { done:false,value:"漫漫" }
console.log(namesIterator.next()); // { done:false,value:"前端" }
console.log(namesIterator.next()); // { done:false,value:undefined }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<script>
const nums = [100, 200, 300, 400, 500]
let index = 0;
let numsIterator = {
next() {
if (index < nums.length) {
return {
done: false,
value: nums[index++]
}
} else {
return {
done: true,
value: undefined
}
}
}
}
console.log(numsIterator.next()); // { done: false, value: 100 }
console.log(numsIterator.next()); // { done: false, value: 200 }
console.log(numsIterator.next()); // { done: false, value: 300 }
console.log(numsIterator.next()); // { done: false, value: 400 }
console.log(numsIterator.next()); // { done: false, value: 500 }
console.log(numsIterator.next()); // { done: false, value: undefined }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<script>
// 封装一个函数,返回一个数组迭代器(Iterator)
const nums = [100, 200, 300, 400, 500]
const names = ["码路", "漫漫", "前端"];
function createArrayIterator(arr) {
let index = 0;
return {
next() {
if (index < arr.length) {
return {
done: false,
value: arr[index++]
}
} else {
return {
done: true,
value: undefined
}
}
}
}
}
let namesIterator = createArrayIterator(names);
console.log(namesIterator.next());
console.log(namesIterator.next());
console.log(namesIterator.next());
let numsIterator = createArrayIterator(nums);
console.log(numsIterator.next());
console.log(numsIterator.next());
console.log(numsIterator.next());
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 66. 可迭代对象
<meta charset="utf-8" />
<script>
// 可迭代对象
// 满足条件1:必须实现一个特定的函数: [Symbol.iterator]
// 满足条件2:这个函数需要返回一个迭代器(这个迭代器用于迭代当前的对象)
// infos就可以叫做可迭代对象
let infos = {
friends: ["码路", "漫漫", "前端"],
[Symbol.iterator]() {
let index = 0;
let infosIterator = {
next() {
if (index < infos.friends.length) {
return {
done: false,
value: infos.friends[index++]
}
} else {
return {
done: true,
value: undefined
}
}
}
}
return infosIterator;
}
};
// 返回了迭代器
// let iterator = infos[Symbol.iterator]();
// console.log(iterator.next());
// console.log(iterator.next());
// console.log(iterator.next());
// console.log(iterator.next());
// 只要是一个可迭代对象,都可以使用for of进行遍历
// infos is not iterable
for (let item of infos) {
console.log(item);
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<script>
// 可迭代对象
let infos2 = {
name: "码路",
age: 18,
height: 1.88,
[Symbol.iterator]() {
let entries = Object.entries(this);
// console.log("entries:", entries);
// iterator迭代器
// iterable可迭代的
let index = 0;
let infos2Iterator = {
next() {
if (index < entries.length) {
return {
done: false,
value: entries[index++]
}
} else {
return {
done: true,
value: undefined
}
}
}
}
return infos2Iterator;
}
};
// 可迭代对象必然具备下面的特点
// const iterator = infos2[Symbol.iterator]()
// console.log(iterator.next());
// console.log(iterator.next());
// console.log(iterator.next());
// console.log(iterator.next());
// 只要是可迭代对象,都可以使用for of
for (let item of infos2) {
console.log(item);
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<script>
// JS中哪些容器天生就是可迭代对象
// 1)数组
// const names = ["aaa", "bbb", "ccc"]
// for (const name of names) {
// console.log(name)
// }
// let iterator = names[Symbol.iterator](); // 得到迭代器
// console.log(iterator.next());
// console.log(iterator.next());
// console.log(iterator.next());
// console.log(iterator.next());
// console.log("---------------");
// 2)Set
// new Set时 ()中只要写可迭代对象都可以
// ["aaa", "bbb", "ccc"] 就是一个迭代对象
// const set = new Set(["aaa", "bbb", "ccc"])
// for (const item of set) {
// console.log(item)
// }
// const setIterator = set[Symbol.iterator]()
// console.log(setIterator.next())
// console.log(setIterator.next())
// console.log(setIterator.next())
// console.log(setIterator.next())
// console.log("---------------");
// 3)arguments
// function foo() {
// for (const arg of arguments) {
// console.log(arg)
// }
// }
// foo(111, 222, 333, 444)
// console.log("---------------");
// 4)字符串
// let str = "hello";
// for (let item of str) {
// console.log(item);
// }
// console.log("---------------");
// 对象不是可迭代对象
// let obj = {
// name: "wc",
// age: 18,
// sex: "man"
// }
// // TypeError: obj is not iterable
// // iterable 可迭代的
// for (let item of obj) {
// console.log(item);
// }
let obj = {
a: 1,
b: 2,
c: 3
}
let res = {
...obj
}
console.log(res);
function fn(x, y, z) {
console.log(x, y, z);
}
fn(obj.a, obj.b, obj.c)
// requires ...iterable[Symbol.iterator] to be a function
fn(...obj)
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# 67. 迭代对象的应用场景
<meta charset="utf-8">
<script>
// 之前说过: 迭代器 可迭代对象
// 可迭代对象的应用场景: 把一个非可迭代对象 转化成 可迭代对象
const info = {
name: "码路",
age: 18,
height: 1.88,
[Symbol.iterator]() {
let values = Object.values(this)
// console.log(values);
let index = 0;
let iterator = {
next() {
if (index < values.length) {
return {
done: false,
value: values[index++]
}
} else {
return {
done: true,
value: undefined
}
}
}
}
return iterator;
}
};
let iterator = info[Symbol.iterator]();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<meta charset="utf-8">
<script>
// 可迭代对象的应用场景: 一些类的构造方法中, 也是传入的可迭代对象
// 语法:new Set([iterable])
const set = new Set(["aaa", "bbb", "ccc"])
console.log(set);
const set2 = new Set("hello")
console.log(set2);
const set3 = new Set(set2)
console.log(set3);
</script>
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
<meta charset="utf-8">
<script>
// 可迭代对象的应用场景: 一些常用的方法
// const p1 = Promise.resolve("aaaa")
// const p2 = Promise.resolve("bbbb")
// const p3 = Promise.resolve("cccc")
// let set = new Set();
// set.add(p1)
// set.add(p2)
// set.add(p3)
// // all([iterable])
// Promise.all(set).then(res => {
// console.log("res:", res);
// })
function bar() {
// arguement不仅是一个伪数组,也是一个可迭代对象
console.log(arguments);
for (let item of arguments) {
console.log(item);
}
}
bar(111, 222, 333);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<meta charset="utf-8">
<script>
// 可迭代对象的应用场景: 自定义类的迭代
class Person {
constructor(name, age, height, friends) {
this.name = name
this.age = age
this.height = height
this.friends = friends
}
// 实例方法
running() {}
[Symbol.iterator]() {
let index = 0;
let iterator = {
next: () => {
if (index < this.friends.length) {
return {
done: false,
value: this.friends[index++]
}
} else {
return {
done: true,
value: undefined
}
}
}
}
return iterator;
}
}
// p1和p2不是可迭代对象
const p1 = new Person("码路", 18, 1.88, ["aaa", "bbb", "ccc"])
const p2 = new Person("漫漫", 30, 1.98, ["111", "222", "333"])
for (const item of p2) {
console.log(item)
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<meta charset="utf-8">
<script>
// 可迭代对象的应用场景: 自定义类的迭代
class Person {
constructor(name, age, height, friends) {
this.name = name
this.age = age
this.height = height
this.friends = friends
}
// 实例方法
running() {}
*[Symbol.iterator]() {
yield* this.friends;
// let index = 0;
// let iterator = {
// next: () => {
// if (index < this.friends.length) {
// return { done: false, value: this.friends[index++] }
// } else {
// return { done: true, value: undefined }
// }
// }
// }
// return iterator;
}
}
// p1和p2不是可迭代对象
const p1 = new Person("码路", 18, 1.88, ["aaa", "bbb", "ccc"])
const p2 = new Person("漫漫", 30, 1.98, ["111", "222", "333"])
for (const item of p2) {
console.log(item)
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 68. 生成器函数
<meta charset="utf-8">
<script>
// 迭代器和可迭代对象学习
// 生成器函数:
// 1.function后面会跟上符号: *
// 2.代码的执行可以被yield控制
// 3.生成器函数默认在执行时, 返回一个生成器对象
// * 要想执行函数内部的代码, 需要生成器对象, 调用它的next操作
// * 当遇到yield时, 就会中断执行
// 生成器函数
function* fn() {
console.log("aaa");
console.log("bbb");
yield
console.log("ccc");
console.log("ddd");
yield
console.log("eee");
console.log("fff");
}
// 调用生成器函数,得到生成器对象
// generator 是生成器的意思
let generator = fn();
// 生成器也有一个next方法
generator.next()
generator.next()
generator.next()
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<meta charset="utf-8">
<script>
// 生成器函数
// yield叫产出 产出数据
function* fn() {
console.log("aaa");
console.log("bbb");
yield "1"
console.log("ccc");
console.log("ddd");
yield "2"
console.log("eee");
console.log("fff");
}
let generator = fn();
// 第1次调用next就执行第1个yield之前的代码
// 第2次调用next就执行第2个yield之前的代码
// ...
console.log(generator.next()); // {value: '1', done: false}
console.log(generator.next()); // {value: '2', done: false}
console.log(generator.next()); // {value: undefined, done: false}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<meta charset="utf-8">
<script>
// 生成器函数
// yield叫产出 产出数据
function* fn() {
console.log("aaa");
console.log("bbb");
let num1 = yield "1"
console.log("num1:", num1);
console.log("ccc");
console.log("ddd");
let num2 = yield "2"
console.log("num2:", num2);
console.log("eee");
console.log("fff");
}
let generator = fn();
// 第1次调用next就执行第1个yield之前的代码
// 第2次调用next就执行第2个yield之前的代码
// ...
// 调用next时,可以给生成器函数传递参数 参数传递到yield前面
console.log(generator.next('666')); // {value: '1', done: false}
console.log(generator.next('888')); // {value: '2', done: false}
console.log(generator.next('333')); // {value: undefined, done: false}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<meta charset="utf-8">
<script>
function* fn() {
let num1 = yield "1"
console.log("num1:", num1);
let num2 = yield "2"
console.log("num2:", num2);
}
let generator = fn();
// 第1次调用next方法,传递的参数,生成器函数是不能接收的
// 第2次调用next方法,传递的参数,传递给了第1个yeild前面的变量
// 第3次调用next方法,传递的参数,传递给了第2个yeild前面的变量
console.log(generator.next('666')); // {value: '1', done: false}
console.log(generator.next('888')); // {value: '2', done: false}
console.log(generator.next('333')); // {value: undefined, done: false}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<meta charset="utf-8">
<script>
function* foo(name1) {
console.log("内部代码:111", name1);
console.log("内部代码:222", name1);
let name2 = yield "aaa"
console.log("内部代码:333", name2);
console.log("内部代码:444", name2);
let name3 = yield "bbb"
console.log("内部代码:555", name3);
console.log("内部代码:666", name3);
yield "ccc"
}
let generator = foo("malu")
console.log(generator.next('666'));
console.log(generator.next('888'));
console.log(generator.next('333'));
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<meta charset="utf-8">
<script>
// 使用生成器 替代 之前讲的 迭代器
let names = ["码路", "漫漫", "前端"];
// 生成器函数
function* createArrayGenerator(arr) {
for (let i = 0; i < arr.length; i++) {
yield arr[i]
}
// yield arr[0]
// yield arr[1]
// yield arr[2]
}
// namesGen生成器对象
let namesGen = createArrayGenerator(names)
console.log(namesGen.next());
console.log(namesGen.next());
console.log(namesGen.next());
console.log(namesGen.next());
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script>
// 使用生成器 生成某个范围的值
// 生成器函数
function* createRangeGenerator(start, end) {
for (let i = start; i <= end; i++) {
yield i
}
}
let rangeGen = createRangeGenerator(2, 5)
console.log(rangeGen.next());
console.log(rangeGen.next());
console.log(rangeGen.next());
console.log(rangeGen.next());
console.log(rangeGen.next());
console.log(rangeGen.next());
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
let names = ["码路", "漫漫", "前端"];
// 生成器函数
function* createArrayGenerator(arr) {
// yield语法糖
yield* arr
// for (let i = 0; i < arr.length; i++) {
// yield arr[i]
// }
// yield arr[0]
// yield arr[1]
// yield arr[2]
}
// namesGen生成器对象
let namesGen = createArrayGenerator(names)
console.log(namesGen.next());
console.log(namesGen.next());
console.log(namesGen.next());
console.log(namesGen.next());
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 69. 异步解决方案
<meta charset="utf-8">
<script>
// 异步解决方案 如果优雅地解决异步问题
// 前置内容:
// promise 迭代器 可迭代对象 生成器
// 异步的解决方案
// 1)回调函数 回调地狱
// 需求:
// 1.发送一次网络请求, 等到这次网络请求的结果 传学生的编号 得到 学生的姓名
// 2.发送第二次网络请求, 等待这次网络请求的结果 传学生的姓名 得到 学生的成绩
// 3.发送第三次网络请求, 等待这次网络请求的结果 传学生的成绩 得到 成绩的等级
function ajax(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(data)
}, 2000)
})
}
// 一层屋嵌套 不优雅
function getData() {
// 第一次发送ajax
ajax("001").then(res1 => {
console.log("res1是学生的姓名");
// 第二次发送ajax
ajax(res1).then(res2 => {
console.log("res2是学生的成绩");
// 第三次发送ajax
ajax(res2).then(res3 => {
console.log("res3是学生的等级");
})
})
})
}
getData()
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<meta charset="utf-8">
<script>
// 异步解决方案 如果优雅地解决异步问题
// 前置内容:
// promise 迭代器 可迭代对象 生成器
// 异步的解决方案
// 1)回调函数 回调地狱
// 2)then链
// 需求:
// 1.发送一次网络请求, 等到这次网络请求的结果 传学生的编号 得到 学生的姓名
// 2.发送第二次网络请求, 等待这次网络请求的结果 传学生的姓名 得到 学生的成绩
// 3.发送第三次网络请求, 等待这次网络请求的结果 传学生的成绩 得到 成绩的等级
function ajax(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(data)
}, 2000)
})
}
// 一层屋嵌套 不优雅
function getData() {
// 第一次发送ajax
ajax("001").then(res1 => {
console.log("res1是学生的姓名");
return ajax(res1)
}).then(res2 => {
console.log("res2是学生的成绩");
return ajax(res2)
}).then(res3 => {
console.log("res3是学生的等级");
})
}
getData()
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<meta charset="utf-8">
<script>
// 异步解决方案 如果优雅地解决异步问题
// 前置内容:
// promise 迭代器 可迭代对象 生成器
// 异步的解决方案
// 1)回调函数 回调地狱
// 2)then链
// 3)生成器函数(要学习生成器函数,必须学习迭代器)
// 需求:
// 1.发送一次网络请求, 等到这次网络请求的结果 传学生的编号 得到 学生的姓名
// 2.发送第二次网络请求, 等待这次网络请求的结果 传学生的姓名 得到 学生的成绩
// 3.发送第三次网络请求, 等待这次网络请求的结果 传学生的成绩 得到 成绩的等级
function ajax(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(data)
}, 2000)
})
}
// 生成器函数
function* getData() {
let res1 = yield ajax("001")
let res2 = yield ajax(res1)
let res3 = yield ajax(res2)
console.log("res3:", "等级");
}
// 下面这一片代码能不能优化呢?
// TJ co
getData().next().value.then(res1 => {
console.log("res1是学生的姓名");
getData().next(res1).value.then(res2 => {
console.log("res2是学生的成绩");
getData().next(res2).value.then(res3 => {
console.log("res3是学生的等级");
// getData().next(res3)
})
})
})
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<meta charset="utf-8">
<script>
// 异步解决方案 如果优雅地解决异步问题
// 前置内容:
// promise 迭代器 可迭代对象 生成器
// 异步的解决方案
// 1)回调函数 回调地狱
// 2)then链
// 3)生成器函数(要学习生成器函数,必须学习迭代器)
// 4)终级方案 async和await
// 需求:
// 1.发送一次网络请求, 等到这次网络请求的结果 传学生的编号 得到 学生的姓名
// 2.发送第二次网络请求, 等待这次网络请求的结果 传学生的姓名 得到 学生的成绩
// 3.发送第三次网络请求, 等待这次网络请求的结果 传学生的成绩 得到 成绩的等级
function ajax(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(data)
}, 2000)
})
}
// 生成器函数
// async和await并不能把同步代码变成异步代码
// async和await 仅仅是生成器的语法糖
// koa1.x中 生成函数有大量使用
// react中 有一些中间件,也用到生成器
// 生成器是必学 迭代器 可迭代对象都需要学
async function getData() {
let res1 = await ajax("001")
console.log("res1是学生的姓名");
let res2 = await ajax(res1)
console.log("res2是学生的成绩");
let res3 = await ajax(res2)
console.log("res3是学生的等级");
}
getData()
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41