JSthree
函数柯里化
这一节咱们来学习函数柯里化,在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
这一节咱们会学习的有:
- 什么是函数柯里化
- 经典面试题
柯里化实际应用
柯里化 作为一种高阶技术, 可以提升函数的复用性和灵活性。
什么是函数柯里化
函数柯里化 (Currying) 是一种将多个参数的函数转换为单个参数函数的技术
转换完毕之后的函数:只传递函数的一部分参数来调用,让他返回一个新的函数去处理剩下的参数。
例子:
1 | // 调整函数 sum |
核心步骤:
sum改为接收一个参数,返回一个新函数- 新函数内部将参数1,参数2累加并返回
1 | function sum(num1) { |
面试回答:
什么是函数柯里化
- 函数柯里化是一种将多个参数的函数转换为单个参数函数的技术
- 转换完毕之后的函数只需要传递一部分参数进行调用,并且会返回一个新的函数去处理剩下的参数
柯里化面试题-全局变量
柯里化在面试的时候一般以笔试题出现,比如
需求:
1 | function sum(a, b, c, d, e) { |
核心步骤:
- 接收不定长参数
- 存储已传递的参数
- 判断长度
- 满足5:累加
- 不满足:继续返回函数本身
1 | let nums = [] |
面试回答:
柯里化面试题-全局变量
- 定义数组保存参数
- 函数接收不定长参数
- 调用时将传入的参数,添加到数组中,并判断数组长度:
- 满足长度要求:累加并返回结果
- 未达到长度要求:继续返回函数本身
柯里化面试题-使用闭包
需求:
- 使用闭包将上一节代码中的全局变量,保护起来
- 支持自定义累加的参数个数
1 | function sumMaker(length){ |
核心步骤:
- 定义外层函数:
- 定义参数
length - 将全局变量迁移到函数内
- 定义参数
- 定义内层函数:
- 参数长度判断,使用传入的参数
length - 直接复用上一节的逻辑,并返回
- 参数长度判断,使用传入的参数
1 | function sumMaker(length) { |
面试回答:
柯里化面试题-使用闭包
- 定义函数,接收参数,用来确定参数个数
- 内部将上一节的逻辑拷贝进去
- 返回原函数
- 通过这样的调整,可以让我们自定义参数的个数,并且没有上一节的全局变量数组
柯里化实际应用-类型判断
通过参数复用,实现一个类型判断生成器函数
需求:
- 将下列4个类型判断函数,改写为通过函数
typeOfTest动态生成
1 | // 有如下4个函数 |
核心步骤:
typeOfTest接收参数type用来接收判断的类型- 内部返回新函数,接收需要判断的值,并基于
type进行判断 - 使用箭头函数改写为最简形式~~传送门-github
1 | const typeOfTest = (type) => { |
面试回答:
柯里化实际应用-类型判断
定义函数,接收需要判断的类型名
内部返回一个新的函数,
- 新函数接收需要判断的具体的值
- 新函数内部根据外层函数传入的类型,以及传入的值进行判断并返回结果
柯里化实际应用-固定参数
依旧是一个参数复用的实际应用
需求:
- 将如下3个请求的函数(都是post请求),变为通过
axiosPost函数动态生成 - 实现函数
axiosPost
1 | // 项目开发中不少请求的 请求方法 是相同的,比如 |
核心步骤:
- 函数内部固定请求方法,post
- 函数内部调用
axios发请求即可 axios内部就是这样实现的
1 | function requestWithMethod(method) { |
面试回答:
柯里化实际应用-固定参数
函数柯里化是一种函数式编程思想:将多个参数的函数转换为单个参数函数,调用时返回新的函数接收剩余参数
常见面试题,将函数改写为如下调用新式:核心思想就是返回新的函数,根据已经记录的参数长度判断:
长度符合要求:累加
长度不符合要求:继续返回
1 | function sum(a, b, c, d, e) { |
常见应用:固定参数,比如
axios中的:- 类型判断函数
- get,post,put等别名方法
- 就用到了柯里化的思想
JS设计模式
这一节咱们来学习JS中的设计模式
设计模式的指的是:在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案。通俗一点说,设计模式就是给面向对象软件开发中的一些好的设计取个名字。
目前说到设计模式,一般指的是《设计模式:可复用面向对象软件的基础》一书中提到的23种常见的软件开发设计模式。
JavaScript中不需要生搬硬套这些模式,咱们结合实际前端开发中的具体应用场景,来看看有哪些常用的设计模式
这一节咱们会学习:
- JS中的常用设计模式
- 设计模式在开发/框架中的应用场景
工厂模式
在JavaScript中,工厂模式的表现形式就是一个直接调用即可返回新对象的函数
1 | // 定义构造函数并实例化 |
应用场景
Vue2->Vue3:
- 启用了
new Vue,改成了工厂函数createApp-传送门 - 任何全局改变 Vue 行为的 API(vue2) 现在都会移动到应用实例上(vue3)
- 就不会出现,Vue2中多个Vue实例共享,相同的全局设置,可以实现隔离
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
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#app1,
#app2 {
border: 1px solid #000;
}
</style>
</head>
<body>
<h2>vue2-全局注册组件</h2>
<div id="app1">
实例1
<my-title></my-title>
</div>
<div id="app2">
实例2
<my-title></my-title>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.7.9/vue.js"></script>
<script>
Vue.component('my-title', {
template: '<h2 style="color:orange">标题组件</h2>'
})
const app1 = new Vue({
el: "#app1"
})
const app2 = new Vue({
el: "#app2"
})
</script>
</body>
</html>- 启用了
axios.create:
- 基于传入的配置创建一个新的
axios实例,传送门 - 项目中有2个请求基地址如何设置?
- 基于传入的配置创建一个新的
1 | // 1. 基于不同基地址创建多个 请求对象 |
面试回答:
工厂模式:JS中的表现形式,返回新对象的函数(方法)
- ```javascript
function sayHi(){} // 函数
const obj ={
}name:'jack', sayHello(){} // 方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2. 日常开发中,有2个很经典的场景
1. `vue3`中创建实例的api改为`createApp`,`vue2`中是`new Vue`
1. Vue3中,没有影响所有Vue实例的api了,全都变成了影响某个app对象的api,比如`Vue.component-->app.component`
2. `axios.create`基于传入的配置,创建一个新的请求对象,可以用来设置多个基地址
### 单例模式
单例模式指的是,在使用这个模式时,单例对象整个系统需要保证**只有一个**存在。
**需求:**
1. 通过静态方法`getInstance`获取**唯一实例**
```javascript
const s1 = SingleTon.getInstance()
const s2 = SingleTon.getInstance()
console.log(s1===s2)//true
- ```javascript
核心步骤:
- 定义类
- 私有静态属性:
#instance - 提供静态方法
getInstance:- 调用时判断
#instance是否存在: - 存在:直接返回
- 不存在:实例化,保存,并返回
- 调用时判断
1 | class SingleTon { |
实际应用:
- vant组件库中的弹框组件,保证弹框是单例
- vue中注册插件,用到了单例的思想(只能注册一次)
面试回答:
- 单例模式:
- 保证,应用程序中,某个对象,只能有一个
- 自己实现核心为一个返回唯一实例的方法,比如
getInstance- 实例存在
->返回 - 实力不存在
->创建,保存->返回
- 实例存在
- 应用场景:
vant的toast和notify组件都用到了单例:多次弹框,不会创建多个弹框,复用唯一的弹框对象vue中注册插件,vue2和vue3都会判断插件是否已经注册,已注册,直接提示用户
观察者模式
在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会自动收到通知。
举个例子:
dom事件绑定,比如
1 | window.addEventListener('load', () => { |
- Vue中的watch:
面试回答:
- 观察者模式重点说清楚2点即可:
- 在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会自动收到通知。
- 常见场景:vue中的watch,dom事件绑定
观察者模式和发布订阅模式的区别也是常见考点,回答方式见下一节
发布订阅模式01-应用场景
发布订阅模式可以实现的效果类似观察者模式,但是两者略有差异,一句话描述:一个有中间商(发布订阅模式)一个没中间商(观察者模式)
应用场景:
发布订阅模式02-自己写一个事件总线
需求:
1 | const bus = new HMEmitter() |
核心步骤:
- 定义类
- 私有属性:
#handlers={事件1:[f1,f2],事件2:[f3,f4]} - 实例方法:
- $on(事件名,回调函数):注册事件
- $emit(事件名,参数列表):触发事件
- $off(事件名):移除事件
- $once(事件名,回调函数):注册一次性事件
基础模板:
1 |
|
1 | class HMEmmiter { |
面试回答:
- 发布订阅模式:可以实现的效果类似观察者模式,但是两者略有差异,一句话描述:一个有中间商(发布订阅模式)一个没中间商(观察者模式)
- 经典的场景是
vue2中的EventBus,vue3移除了实例的$on,$off,$emit方法,如果还需要使用:- 使用第三方插件
- 自己实现事件总线:
- 自己实现事件总线的核心逻辑:
- 添加类,内部定义私有属性
#handlers={},以对象的形式来保存回调函数 - 添加实例方法:
$on:- 接收事件名和回调函数
- 内部判断并将回调函数保存到
#handlers中,以{事件名:[回调函数1,回调函数2]}格式保存
$emit- 接收事件名和回调函数参数
- 内部通过
#handlers获取保存的回调函数,如果获取不到设置为空数组[] - 然后挨个调用回调函数即可
$off- 接收事件名
- 将
#handlers中事件名对应的值设置为undefined即可
$once- 接收事件名和回调函数
- 内部通过
$on注册回调函数, - 内部调用
callback并通过$off移除注册的事件
- 添加类,内部定义私有属性
原型模式
在原型模式下,当我们想要创建一个对象时,会先找到一个对象作为原型,然后通过克隆原型的方式来创建出一个与原型一样(共享一套数据/方法)的对象。在JavaScript中,Object.create就是实现原型模式的内置api
应用场景:
vue2中重写数组方法:
1 |
|
面试回答:
原型模式:
- 基于某个对象,创建一个新的对象
- JS中,通过
Object.create就是实现了这个模式的内置api - 比如
vue2中重写数组方法就是这么做的
vue2中数组重写了7个方法,内部基于数组的原型
Array.prototype创建了一个新对象- 创建的方式是通过
Object.create进行浅拷贝 - 重写的时候:
- 调用数组的原方法,获取结果并返回—-方法的功能和之前一致
- 通知了所有的观察者去更新视图
1 | const app = new Vue({ |
代理模式
代理模式指的是拦截和控制与目标对象的交互
这里我们来看一个非常经典的代理模式的应用: 缓存代理
核心语法:
- 创建对象缓存数据
- 获取数据时,先通过缓存判断:
- 有直接获取
- 没有:调用接口
1 | // 1. 创建对象缓存数据 |
面试回答:
- 代理模式的核心是,通过一个代理对象拦截对原对象的直接操纵
- 比如可以通过缓存代理:
- 缓存获取到的数据
- 拦截获取数据的请求:
- 已有缓存:直接返回缓存数据
- 没有缓存:去服务器获取数据并缓存
- 提升数据获取效率,降低服务器性能消耗
迭代器模式
迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示.简而言之就是:遍历
遍历作为日常开发中的高频操作,JavaScript中有大量的默认实现:比如
Array.prototype.forEach:遍历数组NodeList.prototype.forEach:遍历dom,document.querySelectorAllfor infor of
面试题:
for in和for of的区别?
1 | Object.prototype.objFunc = function () { } |
可迭代协议和迭代器协议:
可迭代协议:传送门
- 给对象增加属方法
[Symbol.iterator](){} - 返回一个符合迭代器协议的对象
- 给对象增加属方法
迭代器协议:传送门
- next方法,返回对象:
{done:true},迭代结束{done:false,value:'xx'},获取解析并接续迭代- 实现方式:
- 手写
Generator
- next方法,返回对象:
1 | // ------------- 迭代协议 ------------- |
面试回答:
- 迭代器模式在js中有大量的默认实现,因为遍历或者说迭代时日常开发中的高频操作,比如
forEach,for in,for of等 for in和for of的区别:如何自定义可迭代对象?








