JS基础
0.1+0.2 != 0.3 的根本原因
0.1和0.2转换成二进制是无限循环的,遵循IEEE754尾数位数限制(小数位52位),需要将后面多余的位截掉,就造成了精度损失。
instanceof 和 typeof
instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。instanceof用于判断一个引用类型的变量是否是某个对象的实例typeof判断基本数据类型的类型。
typeof null 为什么是object
在
javascript的最初版本中,使用的 32位系统,js为了性能优化,使用低位来存储变量的类型信息。
null的机器识别码标识为全0object的机器码低位标识为000- 所以
typeof null的结果被误判为Object,这是一个bug
执行上下文
- 当函数运行时,会创建一个执行环境,这个执行环境就叫执行上下文
- 每个上下文都有一个关联的变量对象(variable object),这个上下文中定义的所有变量和函数都存在这个对象上。
- 当代码执行流进入函数时,函数上下文会被推到一个上下文栈上;上下文中的代码在执行的时候,会创建变量对象的一个作用域链。作用域链决定了各级上下文中的代码在访问变量和函数时的顺序。代码正在执行的上下文的变量对象始终位于作用域链的最前端;如果上下文是函数,则其活动对象用作变量对象。函数执行完之后,上下文栈会弹出该函数上下文,将控制权返还给之前的执行上下文。
- 上下文之间的连接是线性有序的。每个上下文都可以到上一级上下文中去搜索变量和函数,但任何上下文都不能到下一级上下文中去搜索
全局上下文(window对象)是最外层的上下文,所有通过var定义的全局变量和函数都会成为window对象的属性和方法。使用let和const的顶级声明不会定义在全局上下文上。
上下文在其代码执行完毕后会被销毁,包括定义在它上面的所有变量和函数。全局上下文在应用程序退出前才会被销毁。
闭包
定义:当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。有权访问另一个函数作用域中的变量的函数
闭包的本质:函数在执行的时候会放到一个执行栈上,当函数执行完毕之后会从执行栈上移除; 但是堆上的作用域成员因为被外部使用不能释放,因此内部函数依然可以访问外部函数的成员
节流和防抖
- 节流: 在间隔时间之内, 只允许执行一次函数; 这样的函数就是函数节流。
function throttle(func ,delay = 500) {
let timer = null;
return function () {
let context = this
let args = arguments;
if(!timer){
timer = setTimeout(() => {
// 下面这种写法:这里是箭头函数 没有自己的arguments,所以可以接收到来自上层的参数
// func.apply(context, arguments)
func.apply(context, args)
args
timer = null
}, delay)
}
}
}
- 防抖:如果在一定的时间间隔之内去重复执行某个程序,则会把程序执行时间重置。
function debounce(fn, delay = 500) {
let timer = null;
return function () {
let context = this
clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(context, arguments)
}, delay);
}
}
输入一个URL到页面呈现
- 构建请求
- 查找强缓存,如果命中缓存直接使用缓存资源
- 没有命中缓存,进入DNS域名解析系统,把域名解析为ip地址
- 把ip发送到网络供应商,去找对应的主机服务器
- TCP的三次握手建立连接
- 发送请求,取回入口文件index.html
- 开始解析入口文件,并取回需要的资源sources
- 渲染页面
EventLoop执行过程
- 所有同步任务在主线程上依次直接执行,形成一个执行栈(调用栈);异步任务分别放入任务队列(宏任务进入宏任务队列,微任务进入微任务队列)。
- 当执行栈中任务执行完,再去检查微任务队列是否为空,有就执行,直到全部执行完。
- 微任务执行完后,再到任务队列检查宏任务是否为空,有就取出最先进入队列的宏任务压入执行栈中执行其同步代码。
- 然后回到第二部,依次循环,直到所有任务执行完毕。
浏览器和 Node 环境下,microtask 队列的执行时机不同:
- 在浏览器中执行完宏任务后会检查微任务队列并清空微任务,然后再去执行宏任务。
- node 环境下执行宏任务所产生的的微任务被放入微任务队列,然后执行下一个宏任务,最后才执行微任务队列中的微任务。microtask 在事件循环的各个阶段之间执行
宏任务和微任务的代表(那些不常见的了解下)
宏任务
- setTimeout
- setInterval
- Node中的setImmediate
- requestAnimationFrame
- I/O
- UI渲染、网络请求
微任务
- promise.then()
- queueMicrotask()
- MutationObserver(监听DOM)
- node中的process.nextTick等
浏览器的渲染过程
- 解析HTML生成DOM树。
- 解析CSS生成CSSOM规则树。
- 将DOM树与CSSOM规则树合并在一起生成渲染树。
- 遍历渲染树开始布局,计算每个节点的位置大小信息。
- 将渲染树每个节点绘制到屏幕。
cors跨域
- 简单请求
- 非简单请求,会发送一次预检请求,返回码是204,预检通过后才会真正发出请求,这才返回200。前端发请求的时候增加一个额外的headers来触发非简单请求
跨域的解决方案
-
CORS:后端设置
Access-Control-Allow-Origin:*后,表示服务器可以接受所有的请求源(Origin),即接受所有跨域的请求。 -
jsonp
jsonp的原理就是利用
<script>标签没有跨域限制,通过<script>标签src属性,发送带有callback参数的GET请求,服务端将接口返回数据拼凑到callback函数中,返回给浏览器(script标签的src属性请求回来的东西是一个字符串,浏览器会把这个字符串当作 js 代码来执行),浏览器解析执行,从而前端拿到callback函数返回的数据。jsonp的缺点:只能发送get一种请求。
-
服务器代理,通过修改nginx服务器配置实现代理转发。
实现一个动画
-
js直接实现;
let elem = document.getElementById('rect'); let left = 0; let timer = setInterval(function(){ if(left<window.innerWidth-200){ elem.style.marginLeft = left+'px'; left ++; }else { clearInterval(timer); } },16);tips: 我们设置的setInterval时间间隔是16ms。一般认为人眼能辨识的流畅动画为每秒60帧(目前大多数设备的屏幕刷新率为 60 次/秒),这里16ms比(1000ms/60=16.66)帧略小一些,但是一般可认为该动画是流畅的。 在很多移动端动画性能优化时,一般使用16ms来进行节流处理连续触发的浏览器事件。例如对touchmove、scroll事件进行节流等。通过这种方式减少持续事件的触发频率,可以大大提升动画的流畅性。
-
CSS3 transition
transition是过渡动画。但是transition并不能实现独立的动画,只能在某个标签元素样式或状态改变时进行平滑的动画效果过渡,而不是马上改变。
注意:在移动端开发中,直接使用transition动画会让页面变慢甚至卡顿。所以我们通常添加transform:translate3D(0,0,0)或transform:translateZ(0)来开启移动端动画的GPU加速,让动画过程更加流畅。
-
CSS3 animation
animation 算是真正意义上的CSS3动画。通过对关键帧和循环次数的控制,页面标签元素会根据设定好的样式改变进行平滑过渡。而且关键帧状态的控制是通过百分比来控制的。
ES6 的新特性
-
let const 块级作用域
- 不能重复声明
- 不会进行预解析
- 会被所有代码块限制作用范围
- const 在声明时必须被赋值
-
数组解构
-
对象解构
-
模板字符串
-
字符串的新方法:
startsWithendsWithincludes -
展开运算符
spread -
箭头函数
箭头函数就是普通函数的简写,可以更优雅的定义一个函数,和普通函数相比,有以下几个差异:
-
箭头函数不会创建自己的 this,它只会从自己的作用域链的上一层继承 this。
-
不绑定 arguments,当在箭头函数中调用 aruguments 时同样会向作用域链中查询结果,可以用剩余参数 rest 代替。
-
不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。
-
不可以使用 new 操作符,因为:
- 没有自己的 this,调用 bind,call,apply 绑定 this 值无效。
- 没有 prototype 属性 ,而 new 命令在执行时需要将构造函数的 prototype 赋值给新的对象的 proto
-
-
对象字面量,若值是变量,且和属性名相同 可省略
-
Object.is()方法判断两个值是否为同一个值 -
proxy代理对象(或数组)const person = { name: 'xiao', age: 18 } // console.log('name' in person); // 该属性是否属于某个对象 const { log } = console const personProxy = new Proxy(person, { get(target, property) { log(target, property); return property in target ? target[property] : 'default' }, set(target, property, value) { if (property === 'age') { if (!Number.isInteger(value)) { throw new TypeError(`${value} is not an integer`) } } log(target, property, value) } }) console.log(personProxy.name); // personProxy.age = '2' -
Set集合 -
Map对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值) 都可以作为一个键或一个值。 -
for of遍历 -
Reflect是一个内置的对象,它提供拦截JavaScript操作的方法,不能new
this指向
函数的回调(callback)没有绑定任何上下文,因此相当于一个自由变量
var obj1={
num:5,
a:function(){
var arr=[1,2,3,4,5];
arr.forEach((function(item){
// 普通回调函数中this将会被重新指向到window,可用bind改变this指向
console.log(this) // Window
console.log(this.num,item);
}).bind(this)); // 改变this为obj1
arr.forEach(item=>{
console.log(this); //当前箭头函数外this的指向(obj1)
}) // 不能使用bind,apply,call。无效
}
}
obj1.a()
- 事件函数中的this -> 事件侦听的对象;this指向e.currentTarget
- 箭头函数中,call()、apply()、bind()方法无效
//例1,this指向定义箭头函数所在的运行作用域,它位于对象cat内,但cat不能构成一个运行作用域,所以指向全局window
const cat = {
lives: 9,
jumps: () => {
this.lives--;
}
}
//例2,此时this也是指向window,不能动态监听button,改成普通函数后this指向按钮对象。
var button = document.getElementById('press');
button.addEventListener('click', () => {
this.classList.toggle('on');
});
伪数组转换成数组
Array.prototype.slice.call(arguments)[].slice.call(arguments)arr = [...arguments]Array.from(arguments)
原型链相关
Function.prototype.a = () => {
console.log(1);
}
Object.prototype.b = () => {
console.log(2)
}
function A() {}
const a = new A();
a.a(); // TypeError
a.b(); // 2
A.a(); // 1
A.b(); // 2
script元素的defer和async属性的区别
CSS
css定位的几种区别 relative, absolute, fixed,stick
- 相对定位
position:relative;参考物是自己,不脱离文档流(初始位置仍然占据空间),top:100px; 给正值是向该容器的中心点移动; - 绝对定位
position:absolute; 参考物是外层具有position属性的元素, 如果向外都么有找到最后会参考body做定位 - 固定定位
position:fixed; 参考物是可视窗口 - 粘性定位:
position:sticky; 元素根据正常文档流进行定位,该值总是创建一个新的层叠上下文,偏移值不会影响其他任何元素的位置(相对于最近的滚动祖先元素)
BFC「块级格式化上下文」
- 同一个 BFC 下垂直方向的外边距会发生折叠
- BFC 可以包含浮动的元素(清除浮动)
- 不会与浮动的元素相重合(可以阻止元素被浮动元素覆盖)
满足下列条件之一就可以触发BFC
- 1:根元素 html
- 2:float的值不为none
- 3:overflow的值不为visible
- 4:display的值为:flex、inline-block、table-cell、table-caption
- 5:position的值为absolute或者fixed
回流(又叫重排)和重绘
回流:当DOM的变化影响了元素的几何信息(元素的的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做回流。每个页面至少需要一次回流,就是在页面第一次加载的时候。
重绘: 当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘。
注意:回流必将引起重绘,而重绘不一定会引起回流
何时发生回流重绘:
- 添加或删除可见的DOM元素
- 元素的位置发生变化
- 元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
- 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代。
- 激活CSS伪类(例如:
:hover) - 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)
- 设置 style 属性的值,因为通过设置style属性改变结点样式的话,每一次设置都会触发一次reflow
当你获取布局信息的操作的时候,会强制刷新渲染队列,引起回流
比如当你访问以下属性或者使用以下方法:
offsetTop、offsetLeft、offsetWidth、offsetHeight
scrollTop、scrollLeft、scrollWidth、scrollHeight
clientTop、clientLeft、clientWidth、clientHeight
getComputedStyle()
getBoundingClientRect
如果要使用它们,最好将值缓存起来
回流优化建议
-
减少重排范围
- 尽量以局部布局的形式组织html结构,尽可能小的影响重排的范围
- 不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局。那么在不得已使用table的场合,可以设置table-layout:auto;或者是table-layout:fixed这样可以让table一行一行的渲染,这种做法也是为了限制reflow的影响范围。
-
减少重排次数
- 样式集中改变;统一在 cssText 变量中编辑
- 分离读写操作;DOM 的多个读操作(或多个写操作),应该放在一起。
- 将 DOM 离线。使用 display:none将其从页面上拿掉,我们之后的操作就不会触发重排和重绘,等我们变更后,通过
display:block;展示。这样只做了两次重排重绘就可以了。 - 使用 absolute 或 fixed 脱离文档流
- 优化动画
- 可以把动画效果应用到 position属性为 absolute 或 fixed 的元素上,这样对其他元素影响较小。
- 启用GPU硬件加速
-
比起考虑如何减少回流重绘,我们更期望的是,根本不要回流重绘
使用css3硬件加速,可以让transform、opacity、filters这些动画不会引起回流重绘 。但是对于动画的其它属性,比如background-color这些,还是会引起回流重绘的
常见的触发硬件加速的css属性:
- transform
- opacity
- filters
React
redux数据流
- 组件想要改变
Store数据的时候,需要创建一个Action,然后通过dispatch(action)传递给Store然后Store把Action转发给Reducers - 要保证
Reducer是一个纯函数,然后生成一个新的数据传递给Store Store通过触发subscribe()这个方法来调用函数, 执行setState使得视图更新
react中key值的理解
- react利用key来识别组件,它是一种身份标识,相同的
keyreact认为是同一个组件,这样后续相同的key对应组件都不会被创建 - 有了key属性后,就可以与组件建立了一种对应关系,react根据key来决定是销毁重新创建组件还是更新组件。 -key相同,若组件属性有所变化,则react只更新组件对应的属性;没有变化则不更新。
- key值不同,则react先销毁该组件(有状态组件的componentWillUnmount会执行),然后重新创建该组件(有状态组件的constructor和componentWillUnmount都会执行)
React嵌套组件加载顺序
class App extends Component {
componentWillMount() {
console.log('App-页面即将加载')
}
componentDidMount() {
console.log('App-页面加载完成')
}
render() {
return (
<div className="App">
<Header />
<Body />
<Footer />
</div>
);
}
}
// console.log()内容和顺序如下
App-页面即将加载
Header-页面即将加载
body-页面即将加载
Footer-页面即将加载
Header-页面加载完成
body-页面加载完成
Footer-页面加载完成
App-页面加载完成
受控组件和非受控组件
非受控组件
在HTML的表单元素中(input、select、textarea),它们通常自己维护一套state,并随着用户的输入自己进行UI上的更新,这种行为是不被我们程序所管控的。
受控组件
而如果将React里的state属性和表单元素的值建立依赖关系,再通过onChange事件与setState()结合更新state属性,就能达到控制用户输入过程中表单发生的操作。被React以这种方式控制取值的表单输入元素就叫做受控组件。
函数组件和类组件的区别「待完成」
setState 底层原理「待学习」
创建虚拟dom,diff算法对比 React在setState之后,会经对state进行diff,判断是否有改变,然后去diff dom决定是否要更新UI。
React 合成事件(SyntheticEvent)
React使用合成事件,其主要目的有三个:
- 进行浏览器兼容,实现更好的跨平台。
React 采用的是顶层事件代理机制,能够保证冒泡一致性,可以跨浏览器执行。React 提供的合成事件用来抹平不同浏览器事件对象之间的差异,将不同平台事件模拟合成事件。
- 避免垃圾回收
事件对象可能会被频繁创建和回收,因此 React 引入事件池,在事件池中获取或释放事件对象。即 React 事件对象不会被释放掉,而是存放进一个数组中,当事件触发,就从这个数组中弹出,避免频繁地去创建和销毁(垃圾回收) 。
- 方便事件统一管理和事务机制
优化
性能优化
- gzip压缩,压缩效率非常高,通常可以达到 70% 的压缩率
- 去除
console.log,terser-webpack-plugin插件配置到webpack来实现 - 按需加载;资源预加载
- 异步无阻塞加载JS
deferasync(脚本下载完立即执行) - loading菊花图加载,骨架屏效果
- 防抖(debounce)/节流(throttle)
- 扁平化数据,数据驱动视图
- 缓存接口请求回来的数据
- 使用Tinypng压缩图片(Tiny: 读作[ˈtaɪni])
HTTP
HTTP 缓存
HTTP 缓存又分为强缓存和协商缓存:
- 首先通过
http头的Cache-Control和Expire这两个表示资源的缓存时间的字段来验证强缓存是否可用,如果命中强缓存,则不发送请求,直接读取缓存。 - 如果已过期,那么进入协商缓存阶段,发起HTTP请求,这个请求会携带第一次请求返回的有关缓存的header字段信息。
- 通过
If-None-Match头将先前服务器发送过来的Etag发送给服务器,服务器对比Etag是否与服务器的相同,若相同,就将If-None-Match的值设为false,返回状态为304,告诉浏览器使用缓存获取资源。 - 客户端还会通过
If-Modified-Since头将先前服务器端发过来的最后修改时间戳发送给服务器,服务器端通过这个时间戳判断客户端的页面是否是最新的,如果不是最新的,那么返回资源和 200 状态码,如果是最新的,则返回304,客户端继续使用本地缓存。
- 通过
HTTP 常用的状态码及使用场景?
- 1xx:表示目前是协议的中间状态,还需要后续请求
- 2xx:表示请求成功
- 3xx:表示重定向状态,需要重新请求
- 4xx:表示请求报文错误
- 5xx:服务器端错误
常用状态码:
- 101 切换请求协议,从 HTTP 切换到 WebSocket
- 200 请求成功,有响应体
- 301 永久重定向:会缓存
- 302 临时重定向:不会缓存
- 304
NOT Modified协商缓存命中, 通常是在协商缓存中表示本次内容未修改 浏览器缓存 - 400
Bad Request前端请求错误 - 401
Unauthorized未授权 - 403
Fobidden请求被拒绝, 服务器禁止访问 - 404
Not Found资源不存在 - 500
Inter Server Error服务器端错误 - 503
server Unavailable服务器繁忙 | 服务不可用
http和https的区别
http端口号80,https端口号443
HTTPS协议可以理解为HTTP协议的升级,就是在HTTP的基础上增加了数据加密。在数据进行传输之前,对数据进行加密,然后再发送到服务器。这样,就算数据被第三者所截获,但是由于数据是加密的,所以你的个人信息依然是安全的。
webpack
loader和插件plugin的区别
loader 用于转换某些类型的模块,而 plugin 可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。
webpack 压缩插件
uglifyjs-webpack-plugin
构建优化
- webpackPrefetch 预获取,不占用首屏渲染时间;要求浏览器在带宽空闲时预下载当前这个文件,这样实际使用的时候就会从缓存去拿
- webpackChunkName 命名空间,使用它对文件进行命名,命名空间相同的会打包在一个文件里。
const List = lazy(
() => import(/* webpackPrefetch: true, webpackChunkName: "mine" */ 'pages/mine/list'),
);
未归类
小程序的生命周期
- onLoad 监听页面加载
- onShow 监听页面显示
- onReady 监听页面初次渲染完成
- onHide 监听页面隐藏
- onUnload 监听页面卸载
- onPullDownRefresh 监听用户下拉操作
- onReachBottom 页面上拉触底事件的处理函数
- onShareAppMessage 用户点击右上角转发
小程序的路由跳转有哪几种 介绍, navigateBack 可以传参吗
不可以通过url传参,使用getCurrentPages()函数获取页面栈的实例,直接调用上一个页面对象的setData()方法,把数据存到上一个页面中去
vue的v-model数据绑定 对于数组和对象的绑定有什么区别
- 数组的形式时可能无法实时更新数据
axios怎么取消请求 原生中有一个取消的方法
可以调用XMLHttpRequest对象上的abort方法 在axios拦截器里, 查找axios的文档,发现可以通过使用CancelToken来取消axios发起的请求
promise三种状态 pending
- Promise 是异步编程的一种解决方案,对象的状态不受外界影响,一旦状态改变,就不会再变,任何时候都可以得到这个结果,,Promise构造器接受一个函数作为参数,这个函数有两个参数:resolve,reject,分别代表这个Promise实例成功之后的回调函数和失败之后的回调函数。
- 有三种状态: pending(进行中)、fulfilled(已成功)和rejected(已失败)。
- 只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
请求为什么放在ComponentDidMount中
componentDidmount 是在组件完全挂载后才会执行,在此方法中调用setState 会触发重新渲染
对浏览器缓存的理解
是为了加速浏览,浏览器在⽤户磁盘上对请求过的⽂档进⾏存储,当访问者再次请求这个⻚⾯时,浏览器就可以从本地磁盘显示⽂档,这样就可以加速⻚⾯的阅览
DOM事件中target和currentTarget的区别
target是事件触发的真实元素(目标对象)currentTarget是事件绑定的元素(事件侦听的对象)- 事件处理函数中的
this指向是currentTarget
flat() 扁平的
- 该方法创建一个新数组,该数组将所有子数组元素递归连接到指定深度。
var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
编写一个程序将数组扁平化去并除其中重复部分数据,最终得到一个升序且不重复的数组
// 方法一
console.log(Array.from(new Set(arr.flat(Infinity))).sort((a, b) => a - b))
// 方法二
console.log(Array.from(new Set(arr.toString().split(','))).map(Number).sort((a, b) => a - b))
原型链
prototype每一个函数都有一个prototype属性,它指向原型对象constructor:构造器 === 构造函数;表明当前的原型对象属于哪个构造函数 :原型对象只有在构造函数体系中才能使用__proto__原型指针: 这个对象是由谁构造的, 原型指针就指向这个构造函数的原型(prototype)
在创建一个构造函数的时候,自带了一个prototype属性,这个属性指向一个对象,也就是原型对象 这个原型对象⾥⾯有⼀个constructor构造器,它的作⽤是指向创建⾃⼰的构造函数。除此之外prototype还可以存放公共的属性和⽅法 当我们实例化⼀个对象的时候,这个对象⾃带了⼀个 __ proto __ 属性,这个 __ proto __指向创建⾃⼰的构造函数的原型对象。可以使⽤这个原型对象⾥⾯的属性和⽅法
- js代码组织原则: 数据放在实例上, 方法放在原型上
继承「待学习」
- 原型继承
- 借用构造函数继承
- 最常用的是组合继承:融合原型继承+借用构造函数继承的优点
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
}
function SubType(name, age){
// 借用构造函数继承属性
SuperType.call(this, name);
this.age = age;
}
// 通过原型继承方法
// 将一个类型的实例赋值给另一个构造函数的原型
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
alert(this.age);
}
- 寄生式继承
- 寄生组合继承
Function.prototype === Function.proto // true why?
使对象的属性不可更改
Object.freeze(a)
Object.defineProperty(obj, "propery", {
writable: false,
})
// Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
设计模式
-
订阅 发布者模式
class EventEmitter { constructor() { this.events = {}; } on(name, fn) { if (!this.events[name]) { this.events[name] = []; } this.events[name].push(fn); } emit(name, ...args) { if (!this.events[name] || !this.events[name].length) { return; } this.events[name].forEach(callback => callback(...args)); } off(name, fn) { if (!this.events[name] || !this.events[name].length) { return; } this.events[name] = this.events[name].filter(cb => { // 判断若是相同的引用 则去掉。 return cb !== fn }) } die(name) { this.events[name] = []; } } const instance = new EventEmitter(); const fnTest = (data) => { console.log('a', data) } instance.on('a', fnTest); instance.emit('a', 'hello') instance.off('a', fnTest) instance.emit('a', 'world') -
策略模式
策略模式定义了一系列算法,并将每一个算法封装起来,使它们可以相互替换。在前端开发中,这可以用于实现不同的表单验证规则,或者实现不同的布局策略。
策略模式的目的就是将算法的使用和算法的实现分离开来。
-
装饰者模式
装饰者模式允许我们在运行时动态地给对象添加额外的职责,而不改变其结构。在前端开发中,这可以用于添加事件处理程序,或者动态修改 DOM 元素的样式和行为。
比如HOC高阶函数就是装饰器。
-
中介者模式(Mediator Pattern):中介者模式定义了一个对象,这个对象可以封装一组对象的交互方式。这对于处理复杂的、多组件的交互非常有用,例如在实现复杂的用户界面或者单页应用程序时。
-
单例模式(Singleton Pattern):单例模式确保一个类只有一个实例,并提供一个全局访问点。在前端开发中,这可以用于管理应用的状态,例如在 Redux 中的单一状态树。
-
工厂模式(Factory Pattern):工厂模式是一种创建对象的模式,它提供了一种将具体创建逻辑与使用代码分离的方式。在前端开发中,这可以用于创建具有相似结构的多个 DOM 元素,或者创建具有相似行为的多个对象。