文章目录
使⽤过程中遇到的问题,如何解决的
在前端开发过程中,我遇到过的一些常见问题以及解决方式包括:
- 浏览器兼容性问题:使用 Feature Detection 检测浏览器是否支持某功能,并提供降级方案
- 使用 Polyfill 打补丁模拟实现浏览器不支持的 API。
- 性能优化问题:使用懒加载、缓存静态资源、压缩代码、Tree Shaking、SSR 等方式优化页面性能。
- 跨域请求问题:通过 CORS、JSONP、代理服务器等方式实现前后端跨域通信。
- 状态管理问题:使用 Redux、Mobx 等状态管理框架集中管理组件状态。
- 代码调试问题:使用 debugger 语句、breakpoints、Chrome DevTools 等调试工具定位BUG。
- 移动端适配问题:使用 Flex、媒体查询等响应式布局技术,实现组件自适应。
- 模块化问题:使用 Webpack、Rollup、Parcel 等打包工具,实现代码拆分和模块化。
- TypeScript 类型问题:合理的类型定义,编译时类型检查,兼容 JavaScript。
- 测试问题:编写单元测试、UI 测试,保证重构与新增功能的正确性。
JS是什么范式语⾔(⾯向对象还是函数式编程)
- JavaScript 支持面向对象编程:
- 可以定义类和对象,支持封装、继承、多态等面向对象特性
- 可以使用 this 引用对象实例
- 可以使用 new 关键字创建对象
- JavaScript 也支持函数式编程:
- 函数是一等公民,可以赋值给变量,作为参数,也可以作为返回值
- 支持匿名函数和闭包
- 可以使用高阶函数
- 支持不可变数据
- 函数没有副作用
综上所述,JavaScript 融合了面向对象编程和函数式编程的特点,是一个多范式的语言。
koa原理,为什么要⽤koa(express和koa对⽐)
Koa 的工作原理可以总结为以下几点:
- 利用洋葱模型(Onion Model)组织中间件。Koa 将一系列的中间件放入一个栈中,形成一个洋葱圈。请求从外到内,依次通过每一层中间件,响应则从内到外依次执行。
应用程序、上下文、请求及响应这四个对象。应用程序提供核心函数,上下文(Context)将 req 和 res 封装到单个对象,提供给中间件访问,请求(Request)和响应(Response)对象包含请求和响应的详细信息。 - 异步中间件通过 async/await 实现。Koa 应用是一个包含一组中间件函数的对象,每个中间件是一个 async 函数,它接收 ctx 和 next 作为参数。通过 await next() 来执行下一个中间件。
- 函数式编程。洋葱模型采用函数式编程方式,通过剥洋葱一样的一层层调用,使代码逻辑更清晰。
- 洋葱模型中间件执行流程:
- 最内层中间件首先执行。
- 执行完调用 next 触发后续中间件。
- 当后续中间件执行完,会回到上一层中间件。
- 直到所有中间件递归完毕,响应结果发送给客户端。
- 支持请求数据流化。可以通过 yield 将请求数据转换为流,这样可以处理大文件上传等场景。
综上,Koa 通过洋葱模型、async/await、上下文设计等使异步代码更优雅,增强了 Node.js Web 应用框架的表达力。
Koa相比Express有以下几点主要优势:
- Koa使用async/await,代码更直观易读;Express还需要回调函数。
- Koa更容易编写中间件,通过cascade可以组合中间件实现复杂逻辑。
- Koa基于ES6设计, Express基于ES5设计。Koa代码结构更优雅。
- Koa开箱即用支持异步,Express需要配合generator才能实现异步。
使⽤的koa中间件
- koa-router - 路由中间件
- koa-bodyparser - 解析请求body的中间件
- koa-static - 静态资源服务中间件
- koa-views - 模板渲染中间件
- koa-session - session管理中间件
- koa-jwt - jwt认证中间件
- koa-helmet - 安全头设置中间件
- koa-compress - gzip压缩中间件
- koa-logger - 日志中间件
- koa-cors - 跨域资源共享中间件
这些中间件对Koa应用功能的扩展提供了很多便利,可以组合使用实现路由、参数解析、模板渲染、认证、日志、压缩等功能。
Koa的中间件机制也让应用程序的扩展变得非常灵活。通过不同的中间件可以快速定制Koa应用的功能。
ES6使⽤的语法
ES6新增了许多语法特性,主要的有:
- let 和 const - 块级作用域变量
- 箭头函数 - 创建匿名函数的简洁语法
- 模板字符串 - 使用反引号(`)定义多行字符串,并支持在字符串中嵌入表达式
- 扩展运算符 - 展开数组和对象的简写语法 …
- 参数默认值 - 设置函数参数的默认值
- rest 参数 - 获取函数不定参数数组
- 解构赋值 - 从数组或对象中提取值的简洁语法
- 类 - 使用class关键字创建类
- 模块 - 使用export和import实现ES模块
- 迭代器 - 使用for…of遍历数据
- 生成器 - 生成器对象可以暂停函数并返回值
- Promise - 更优的异步编程方案,避免回调函数地狱
- async/await - 基于Promise实现同步方式编写异步代码的新语法
这些新增的语法使得JavaScript代码更简洁、易读、表达力更强,是ES6对语言的重大升级。
Promise 和 async/await 和 callback的区别
Promise、async/await 和 callback 都是实现异步编程的方式,但有以下几点主要区别:
- Callback 是最基本的实现异步的方式,但会导致回调地狱。
- Promise可以链式调用then方法,避免回调地狱,但代码冗余。
- async/await是异步代码的语法糖,使异步代码看起来像同步代码,代码 simplest。
- Promise是基于回调实现,async/await语法依赖Promise。
- Promise可以并行执行异步任务,async/await需要await上一个任务完成。
- Exception处理:
- Callback需要检查err参数
- Promise通过catch捕获
- async/await用try/catch块
- Execution顺序:
- Callback无法保证执行顺序
- Promise通过then链保证顺序
- async/await代码顺序就是执行顺序
- 总结:
- Callback最大缺点是回调地狱
- Promise改进了异步编程,但还是需要then链
- async/await语法上最优雅,使异步代码顺序清晰
所以推荐优先使用async/await,其次是Promise,避免回调函数。
Promise有没有解决异步的问题(promise链是真正强⼤的地⽅)
Promise对于解决异步编程问题,具有重要的进步和意义,但不能说完全解决了异步的问题。
Promise的优点是:
- 比回调函数嵌套更加优雅、简洁
- 可以链式调用then方法,解决回调地狱问题
- 支持错误捕获和处理
- 支持多个异步任务组合执行
Promise不足:
- 无法取消Promise,一旦新建就会执行,无法中途撤销。
- 如果不设置回调函数,Promise内部抛出的错误不会反应到外部。
- 当存在异步依赖时,代码还是会有回调缩进。
- 多个Promise无法确保执行顺序。
而Promise真正强大的地方在于,它可以链式调用多个then和catch方法。
这使得我们可以将异步任务串联起来,实现从一个任务的执行结果,到下一个任务的输入的流程控制。
同时,也可以将多个异步任务的结果合并处理。
所以,Promise解决了回调地狱问题,但不是完全解决异步的最佳方案,它的强大之处在于可以链式组合多个异步任务。异步编程仍有提升的空间,async/await的出现也是对Promise的进一步改进
Promise和setTimeout的区别(Event Loop)
- Promise 是一种异步编程的解决方案,而 setTimeout 是 JS 运行时提供的延时执行函数的机制。
- Promise 的执行是微任务(microtask),setTimeout 的执行是宏任务(macrotask)。
- Event Loop 在每次循环时,会先执行所有的微任务,然后再执行一个宏任务。
- 因此 Promise 的回调函数拥有更高的执行优先级,会先于 setTimeout 执行。
- 如果一个 Promise 的resolve/reject函数在当前任务的末尾,那么其回调会在下一轮 Event Loop 执行。而 setTimeout 的延时仅从当前宏任务结束时开始计算,下一轮 Event Loop 才会执行。
一句话,Promise 相比 setTimeout 具有更高的执行优先级,能够更快速地得到执行。
所以在需要确保函数执行顺序的情况下,推荐使用 Promise;否则可以使用 setTimeout 实现延迟执行。但两者的区别在于执行时机不同,需要根据 Event Loop 机制来理解。
进程和线程的区别(⼀个node实例就是⼀个进程,node是单线程,通过事件循环来实现异步 )
- 进程具有独立的地址空间,线程共享进程的地址空间。
- 进程之间通信复杂,需要IPC;线程可以直接读写进程数据段。
- 线程更轻量,创建和销毁的 overhead 更小。
- 一个进程可以包含多个线程,同一个进程的线程可以并发执行。
- 进程间切换成本高,线程间切换低。
对于 Node.js
每个 Node.js 程序启动至少有一个进程。
Node.js 进程内部是单线程运行的,通过事件循环机制实现异步非阻塞IO。
可以通过 cluster 模块创建多个进程来利用多核 CPU。
一个 Node.js 进程可以服务很多客户端请求,充分利用单线程的高效率。
计算密集型任务可以通过 worker threads 在线程池中处理。
所以总结为:
Node.js 程序是一个进程
进程内部是单线程+事件循环的非阻塞异步模型
可以通过多进程+多线程来充分利用硬件资源
介绍下DFS深度优先
从根节点或者指定节点出发,沿着一个方向遍历图/树的每个分支。
当一个分支走到尽头时,回溯并遍历下一个未访问过的分支。
对每个节点只访问一次,直到所有节点都被访问过为止。
- DFS 的主要特点是:
- 可以利用栈的先入后出特性实现。
- 能够找到一条路径的尽头,所以可以用来判断图的连通性。
- 由于不考虑节点的广度,不能找到最短路径。
- 适合用来解决拓扑排序,组合问题等。
DFS 在代码实现上主要通过递归或者栈来实现,关键点是通过判断节点的访问情况来决定下一步访问哪个节点,避免重复访问。
DFS 在很多场景下都是一种有效的搜索算法,比如游戏地图的探索、程序运行路线的搜索等。与其相对的是 BFS 广度优先搜索算法。
介绍下观察者模式
观察者(Observer)模式也叫发布-订阅(Publish-Subscribe)模式,定义对象之间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。
- 观察者模式主要包含以下角色:
- Subject: 目标对象,可以添加、删除、通知观察者对象。
- Observer: 观察者对象,用来接收并响应目标对象的通知。
观察者模式的实现步骤: - Subject对象提供注册和移除观察者的接口。
- Subject对象提供通知观察者的接口。
观察者对象需要实现更新接口以便在收到通知时更新自身。
目标对象通过调用观察者的更新接口来通知变化。
- 观察者模式适用场景:
- 一个对象的改变需要通知其他多个对象
- 需要实现多个对象之间的发布-订阅关系
- 观察者模式可以降低目标与观察者的耦合,目标对象只需要维护一份观察者列表即可。
- 从前端开发角度来看,观察者模式主要可以应用在以下场景:
- DOM事件处理
可以用观察者模式实现节点的事件绑定和事件触发,比如点击、滚动、输入等事件。节点作为主题(Subject),绑定该节点的回调函数作为观察者(Observer)。 - 自定义事件
可以基于观察者模式实现发布-订阅的自定义事件,如用事件总线连接不同组件。组件可以订阅关心的事件,也可以向事件总线发布事件。 - 数据绑定
用观察者模式实现对模型数据的绑定,当数据变化时自动更新视图。比如Vue中的数据绑定是通过Observer来实现的。 - Promise
Promise 的实现也是观察者模式,promise对象为主题,then/catch为观察者,状态改变时自动调用观察者。 - 状态管理
Redux 和 Vuex 都是基于观察者模式实现的状态管理,Store为主题,组件为观察者。状态变化时触发重新渲染。
- DOM事件处理
综上,观察者模式广泛应用于前端开发,通过主题与观察者解耦,是实现发布-订阅、事件处理、数据绑定的有效设计模式。
观察者模式⾥⾯使⽤的数据结构(不具备顺序 ,是⼀个list)
在观察者模式中,Subject用于保存观察者对象,主要使用的数据结构是数组或列表。
具体来说,Subject需要维护一个观察者列表,来存放所有订阅了该Subject的观察者对象。当Subject状态变化时,需要遍历这个列表,并通知每个观察者。
- 使用数组或列表的主要优点是:
- 可以方便地保存观察者对象
- 可以按顺序遍历通知每个观察者
- 可以 easily添加和删除观察者
- 查找观察者的时间复杂度为 O(n)
- 与数组比较:
- Set 可以确保观察者唯一,但无法保证顺序
- Map 可以通过Key来标识观察者,但查询时间复杂度为 O(1)
所以综合考虑,使用数组或列表存放观察者是比较简单直接的选择。
另外,如果考虑到需要频繁地添加、删除观察者的场景,也可以选择链表来实现,可以在 O(1) 时间内添加和删除元素。
所以综上,Subject的数据结构选型需要根据具体应用场景来确定。但基本来说,数组和列表足以满足大部分观察者模式的需求。
扩展:
如何在Koa中处理异步请求?
Koa利用async/await可以优雅地处理异步请求:
通过async标记请求处理函数,直接使用await调用异步方法,避免了回调地狱。
如何在Koa中处理错误?
可以通过try/catch来捕获全局错误:
Koa路由的主要实现方式有哪些?
常见的Koa路由实现包括:
- koa-router: Koa官方推荐的路由中间件
- koa-route: 简单的路由模块
- koa-joi-router: 结合Joi的参数校验
我们可以根据需求选择不同的路由方案。