前端知识
2022-07-11 11:34:40 4 举报
AI智能生成
前端面试必考知识
作者其他创作
大纲/内容
基础
js运行环境
内核
tradent
EdgeHTML
webkit
blink
Gecko
引擎
渲染引擎
Js引擎
进程 vs 线程
进程是 CPU资源分配的最小单位;线程是 CPU调度的最小单位
一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线。一个进程的内存空间是共享的,每个线程都可用这些共享内存
浏览器打开一个Tab时,创建了一个进程,该进程包含多个线程
GUI 渲染线程
JavaScript引擎线程
定时触发器线程
事件触发线程
异步http请求线程
缓存机制
强制缓存
通过Cache-Control和Expires控制,优先级:Cache-Control > Expires
Expires: 缓存的到期时间,HTTP/1.0的字段,有时间误差,需和Last-Modified结合使用
Cache-Control: private/public/no-cache(使用协商)/no-store(既不使用强制也不使用协商)/max-age=xxx (xxx is numeric,秒级)
存放位置:memory cache(随着进程的释放而释放) 和 disk cache
缓存的顺序:memory –> disk
协商缓存
Response header: Last-Modified 和 Request header: If-Modified-Since
Response header: Etag 和 Request header: If-None-Match
304代表客户端请求的资源缓存有效
LRU 缓存淘汰策略
Least Recent Used,设计思路是,可以使用 HashMap 存储 key,这样可以做到 save 和 get key的时间都是 O(1),而 HashMap 的 Value 指向双向链表实现的 LRU 的 Node 节点
垃圾回收(Garbage Collection)
机制
垃圾收集器(GC)会定期(周期性)找出那些不在继续使用的变量(局部变量),然后释放其内存
哪些变量不再使用:标记清除和引用计数(会出现循环引用的问题,需要自己手工解除循环引用)
发生GC时,需要停止其它操作和响应。
优化:分代回收(临时对象区和持久对象区)、增量回收
泄漏场景
DOM/BOM对象泄漏
script中存在对DOM/BOM的引用
通常由闭包导致,比如事件处理回调,导致DOM对象和脚本中对象双向引用,这个时常见的泄漏原因
排查
DOM中的 addEventLisner 函数及派生的事件监听, 比如 Jquery 中的 on 函数, + vue 组件实例的 $on 函数,第三方库中的初始化函数
其它BOM对象的事件监听, 比如websocket 实例的on 函数
避免不必要的函数引用
如果使用 render 函数,避免在html标签中绑定DOM/BOM 事件
数据存储
cookie
一个域名下存放的cookie的个数一般为20个
每个cookie存放的内容大小一般为4KB
localStorage
HTML5新方法;大小大概是5M;受同源策略的限制
相同的协议、主机名、端口下,就能读取/修改到同一份
sessionStorage
除了协议、主机名、端口外,还要求在同一窗口(也就是浏览器的标签页)下
输入URL后
DNS解析(如果输入的地址不是ip),把域名解析为 IP 地址
通过 Socket 发送数据,TCP连接(三次握手)
浏览器向服务器发送HTTP请求
服务器处理请求并返回HTTP报文
浏览器接受 HTTP 响应,检查 HTTP header 里的状态码,并做出不同的处理方式
如果是可以缓存的,这个响应则会被存储起来
解码
浏览器解析渲染页面
发送异步请求
关闭TCP连接或继续保持连接
堆栈
栈
计算机为原始类型(number、string...)开辟的一块内存空间
堆
计算机为引用类型(object)开辟的一块内存空间
this指向
new 调用:绑定到新创建的对象,注意:显式return函数或对象,返回值不是新创建的对象,而是显式返回的函数或对象
call 或者 apply( 或者 bind) 调用:严格模式下,绑定到指定的第一个参数。非严格模式下,null和undefined,指向全局对象(浏览器中是window),其余值指向被new Object()包装的对象
对象上的函数调用:绑定到那个对象
普通函数调用: 在严格模式下绑定到 undefined,否则绑定到全局对象
canvas优化
离屏缓冲区(离屏canvas)
关闭透明度
避免浮点数的坐标
不要频繁地调用比较耗时的API(shadow相关、drawImage、putImageData)
渲染绘制操作不要频繁调用(stroke()、fill)
尽量少的改变状态机 ctx的里状态
避免阻塞(web worrk)
作用域
函数定义时创建,非调用时
查找过程:函数自身本地变量—>自身父级函数变量—>再高一级函数中的变量—>...—>直至全局变量
闭包
函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性称为闭包
函数A返回了一个函数B,并且函数B使用了函数A的变量,那么函数B就被称为闭包
作用
读取函数内部的局部变量
让这些变量的值始终保持在内存中
应用
匿名自执行函数
结果缓存(函数柯里化利用了这一点)
模拟私有方法
实现类和继承
缺点
内存泄漏
父函数作为对象时变量被修改的副作用
原型
每个对象拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性。原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链
获取某个对象原型的两种方式:Object.getPrototypeOf([]) === [].__proto__
创建对象方式
通过语法结构
通过构造器
通过Object.create()
通过class
拓展原型链
New-initialization
可能会将不需要的方法放在对象上
Object.create
不支持 IE8 以下的版本
允许一次性地直接设置 __proto__ 属性
允许创建一个没有原型的对象
Object.setPrototypeOf
支持所有现代浏览器和微软IE9+浏览器
允许动态操作对象的原型
动态设置原型干扰了浏览器所有的优化,应该抛弃
__proto__
不具备性能可言,应该抛弃
类型判断
typeof()
返回值是一个字符串,是对应的数据类型
null 类型会被返回 object 类型,因为机器码的原因
返回的数据类型是跟进机器码后三位来判断的
object 类型又分为 object 和 function 两种返回
instanceof
返回值是一个bool值
检测的是原型,并不能直接判断是哪种类型
Object.prototype.toString.call()
类型:Null、Boolean、Undefined、Number、String、Symbol、Bigint、Object
事件循环机制
由js单线程决定,分为同步任务和异步任务
同步任务都在主线程执行,形成一个执行栈
主线程之外,事件触发线程管理着一个任务队列,用来放异步任务,异步任务分为宏任务和微任务
同一个执行队列产生的微任务总是会在宏任务之前被执行
macrotask:主代码块、setTimeout、setInterval、requestIdleCallback等(可以看到,事件队列中的每一个事件都是一个 macrotask,现在称之为宏任务队列)
microtask:Promise.then、process.nextTick(Node.js环境)、MutationObserver等
执行顺序:主代码块 > Promise(主体) > Promise(异步then)> requestAnimationFrame > setTimeout(0秒) > requestIdleCallback
事件传播
捕获
先触发外部元素的事件,再触发内部元素
冒泡
先触发内部元素的事件,再触发外部元素
如何阻止:e.stopPropagation || e.cancelBubble = true
事件绑定的方式
使用html内联
优先级最高
DOM 0级标准
只能绑定一个
使用.onclick的方式
DOM 0级标准
只能绑定一个
使用事件监听addEventListener的方式
可以绑定多个事件
支持设定事件传播方式
addEventListener第三个参数
默认值为 false, 即冒泡传递
为 true 时, 事件使用捕获传递
继承
借用构造函数
组合继承
原型继承
寄生式继承
寄生组合式继承
数组去重
用临时对象存储,遍历去重(数字和字符串不行)
用临时数组存储,遍历时indexOf判断去重(NaN不行)
用临时数组存储,遍历时includes判断去重(对象不行)
用临时数组存储,遍历时findIndex判断去重,需用JSON.stringfy
用[…new Set(arr)](空对象不行)
用Array.from(new Set(arr))(空对象不行)
用map.set存储,遍历时map.has判断去重
ES6+
ES6
箭头函数
关键字function的简写
与包围它的代码共享同一个this,解决this的指向问题
参数默认值,不定参数,拓展参数
类(class)
模块(Module)
字符串模板
解构赋值
let与const 关键字
let有暂时性死区的特性,不能在声明前访问
for of 值遍历
Promises
如何停止一个Promise链
发生Big ERROR后return一个Promise,但这个Promise的executor函数什么也不做,这就意味着这个Promise将永远处于pending状态
Promise链上返回的最后一个Promise出错了怎么办
Math,Number,String,Object 的新API
Symbol
一种基本类型,像数字,字符串和布尔一样,它不是一个对象
作用
独一无二的对象属性名(不能用点运算符)
可以实现元编程(内置的11个 Symbol 值)
消除魔术字符串(多次出现、与代码形成强耦合的某一个具体的字符串或者数值)
Proxies
Iterators(迭代器)
Generators(生成器)
Decorator(修饰器/装饰器)
一种与类(class)相关的语法,用来注释或修改类和类方法
装饰器对类的行为的改变,是代码编译时发生的,而不是在运行时
只能用于类和类的方法,不能用于函数,因为存在函数提升
Map + Set + WeakMap + WeakSet
Set:类似与数组,但成员是唯一且无序
WeakSet:允许你将弱引用对象储存在一个集合中
WeakSet 只能储存对象引用,不能存放值,而 Set 对象都可以
WeakSet 对象中储存的对象值都是被弱引用的,即垃圾回收机制不考虑 WeakSet 对该对象的应用,如果没有其他的变量或属性引用这个对象值,则这个对象将会被垃圾回收掉(不考虑该对象还存在于 WeakSet 中)
Map:与Set可以储存不重复的值,但是以 [key, value] 的形式储存
WeakMap:对象是一组键值对的集合,其中的键是弱引用对象,而值可以是任意
ES7
Array.prototype.includes()
指数操作符:Math.pow(2, 10) === 2**10
ES8
async/await
Object.values()
Object.entries()
String padding
函数参数列表结尾允许逗号
Object.getOwnPropertyDescriptors()
ES9
异步迭代
Promise.finally()
Rest/Spread 属性
正则表达式命名捕获组(Regular Expression Named Capture Groups)
正则表达式反向断言(lookbehind)
正则表达式dotAll模式
正则表达式 Unicode 转义
ES10
Object.fromEntries()
trimStart() and trimEnd()
flat() and flatMap()
Symbol 对象的 description 属性
可选的 catch 异常变量
ES11
String 的 matchAll 方法
动态导入语句 import()
import.meta,会返回一个对象,有一个 url 属性,返回当前模块的url路径,只能在模块内部使用。
export * as XX from 'xx module'
Promise.allSettled(所有的 promise 都结束的时候做一些操作,而并不在乎它们是成功还是失败)
一种新的数据类型:BigInt (数据必须添加后缀 n)
globalThis 获取顶层对象
运算符:??
可选链操作符 ?.
ES6+ 代码转成 ES5
将代码字符串解析成抽象语法树,即所谓的 AST
对 AST 进行处理,在这个阶段可以对 ES6 代码进行相应转换,即转成 ES5 代码
根据处理后的 AST 再生成代码字符串
babel工作原理
使用 @babel/parser 的 parse 方法,将代码字符串解析成 AST
使用 @babel/core 的 transformFromAstSync 方法,对 AST 进行处理,将其转成 ES5 并生成相应的代码字符串
过程中,可能还需要使用 @babel/traverse 来获取依赖文件等
TS
any vs unknown
any相当于放弃静态检查,可以调用方法
unknown不可以调用方法
跨域
同域是指域名,协议,端口均相同
JSONP(单向跨域)
原理:用户传callback参数给服务端,服务端返回数据时用该参数作为函数名包着数据
CORS(单向跨域)
四个特定header:Credentials、Headers、Methods、Origin
服务器代理(单向跨域)
websocket
HTML5 的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案
postMessage
window.postMessage方法,允许跨窗口通信,不论这两个窗口是否同源。
安全
跨站点脚本攻击(XSS)
移除用户上传的DOM属性,如onerror等,移除用户上传的style、script、iframe标签等
对用户输入的代码标签进行转换(html encode)
对url中的参数进行过滤
对动态输出到页面的内容进行HTML编码
服务端对敏感的Cookie设置 httpOnly 属性,使js脚本不能读取到cookie
CSP 即是 Content Security Policy
跨站点请求伪造攻击(CSRF)
使用post请求:涉及敏感操作的请求改为POST请求
检查请求头中的Referer参数确保请求发自正确的网站
创建一个唯一的令牌(Token)
使用验证码
网络劫持攻击
https
被iframe检查
内联事件及内联脚本拦截
静态和静态脚本拦截(window.MutationObserver监听在某个范围内的 DOM 树发生变化)
重写 setAttribute 与 document.write,锁死 apply 和 call
其它:控制台注入代码、资源枚举、钓鱼
性能优化
浏览器层
突破浏览器单线程渲染解析机制
利用事件冒泡特性
避开cookie性能bug
突破浏览器并发连接限制
利用GPU硬件加速渲染
HTTP层
减少请求
资源合并(css、js、雪碧图)
字体图标
图片Base64
图片延时加载
缓存方案
减轻请求
资源压缩
服务器开启Gzip压缩
图片服务器动态响应方案
预加载方案
http协议升级,提高对TCP连接利用率
代码层
突破JS单线程限制
重点优化代码中对循环结构体
优化高频事件触发频率
防抖
意味着N秒内函数只会被执行一次,如果N秒内再次被触发,则重新计算延迟时间
input 搜索防抖,如何处理中文输入
compositionstart 事件,中文输入法时在打拼音时(此时input内还没有填入真正的内容),会首先触发
compositionupdate 事件,每打一个拼音字母时触发
compositionend 事件,最后将输入好的中文填入input中时触发
触发compositionstart时,文本框会填入 “虚拟文本”(待确认文本),同时触发input事件;在触发compositionend时,就是填入实际内容后(已确认文本),所以这里如果不想触发input事件的话就得设置一个bool变量来控制。
截流
高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率
主动做内存回收和事件解绑
合理使用设计模式
避免发生回流渲染(回流一定会触发重绘,而重绘不一定会回流)
requestAnimationFrame()代替setInterval
requestAnimationFrame会在浏览器下次重绘之前调用
requestAnimationFrame的回调时机也是在当前的tick中,所以不属于宏任务,但也不是微任务,排在微任务之后
性能指标
响应速度:页面初始访问速度 + 交互响应速度
页面稳定性:页面出错率
外部服务调用:网络请求访问速度
监控的分类
合成监控(Synthetic Monitoring,SYN):采用 web 浏览器模拟器来加载网页,通过模拟终端用户可能的操作来采集对应的性能指标,最后输出一个网站性能报告。例如:Lighthouse、PageSpeed、WebPageTest、Pingdom、PhantomJS 等。
真实用户监控(Real User Monitoring,RUM):一种被动监控技术,是一种应用服务,被监控的 web 应用通过 sdk 等方式接入该服务,将真实的用户访问、交互等性能指标数据收集上报、通过数据清洗加工后形成性能分析报表。例如 FrontJs、oneapm、Datadog 等。
白屏时间
页面开始展示的时间点-开始请求时间点
开始请求时间点:可以通过Performance Timing.navigation Start
页面开始展示的时间点:(1)通过在head标签的末尾插入script来统计时间节点作为页面开始展示时间节点;(2)也有忽略head解析时间直接用Performance Timing.dom Loading 来表示
首屏时间
首屏内容渲染结束时间点-开始请求时间点
首屏模块标签标记法
由于浏览器解析HTML是按照顺序解析的,当解析到某个元素的时候,觉得首屏完成了,就在此元素后面加入script计算首屏完成时间
统计首屏内加载最慢的图片/iframe(更常用)
在 DOM树 构建完成后会通过遍历首屏内的所有图片标签,并且监听所有图片标签 onload 事件,最终遍历图片标签的加载时间获取最大值,将这个最大值作为首屏时间
可交互时间
用户可以正常进行事件输入时间点-开始请求时间点
PerformanceTiming有一个domInteractive属性,代表了DOM结构结束解析的时间点,就是Document.ready State属性变为“interactive”
网络
TCP
面向连接的、可靠的、基于字节流的传输层通信协议
四元组
源地址、源端口、目的地址、目的端口
三次握手
作用
确认双方的接收能力和发送能力是否正常
指定自己的初始化序列号为后面的可靠性传送做准备
交换TCP窗口大小信息
过程
客户端随机生成序号client_isn,SYN为1,状态为SYN_SENT,不包含应用层数据
服务端随机生成序号server_isn,SYN为1,ACK为1,头部确认应答号为client_isn+1,状态为SYN_RCVD
客户端把ACK为1,头部确认应答号为server_isn+1,可携带数据,状态为ESTABLISHED
为什么三次
才可以阻止重复历史连接的初始化
才可以同步双方的初始序列号
才可以避免资源浪费
如何解决网络包乱序
建立连接时随机生成序列号,通过SYN包传给服务端,每发送一次数据,就「累加」一次该「数据字节数」的大小
四次挥手
过程
客户端,FIN为1,状态为FIN_WAIT_1
服务端接受到FIN后,ACK为1,状态为CLOSE_WAIT
客户端接收到ACK后,状态为FIN_WAIT_2。服务端数据处理完后,FIN为1,状态为LAST_ACK
客户端接收到FIN后,回复ACK,状态为TIME_WAIT。服务器接收到ACK后,状态进入CLOSE。客户端经过2MSL后,自动进入CLOSE状态
TIME-WAIT
防止具有相同「四元组」的「旧」数据包被收到
保证「被动关闭连接」的一方能被正确的关闭,即保证最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭
半连接队列(SYN队列)
服务端接收到SYN包并向客户端发出确认,正在等待客户的确认包,这些连接在服务器处于Syn_RECV状态
UDP
User Data Protocol,用户数据报协议,不提供复杂的控制机制,利用 IP 提供面向「无连接」的通信服务
头部只有 8 个字节( 64 位)
TCP VS UDP
连接:TCP面向连接;UDP不需要连接
服务对象:TCP是一对一;UDP是一对一、一对多、多对多
可靠性:TCP是可靠交付数据;UDP 是尽最大努力交付,不保证可靠交付数据
拥塞控制、流量控制:TCP 有拥塞控制和流量控制机制,保证数据传输的安全性;UDP 则没有
首部开销:TCP 首部长度较长,会有一定的开销,首部在没有使用「选项」字段时是 20 个字节,如果使用了「选项」字段则会变长的;UDP 首部只有 8 个字节,并且是固定不变的
DNS
Domain Name System,把域名解析成ip
查询报文使用UDP(53端口),服务器之间备份使用TCP
根域名服务器全球只有13台
过程
搜索浏览器DNS缓存
搜索操作系统的host文件
搜索操作系统的DNS缓存
搜索路由器的DNS缓存
LDNS查询自己的 DNS 缓存
递归查询:根域名服务器.()——>.com顶级域服——>qq.com二级域——直至找到www.qq.com三级域服务器
HTTP
状态码
301(永久重定向),请求方法有时候会被客户端错误地修改为 GET 方法
308(永久重定向) http1.1新增,请求方法和消息主体不会发生改变
302(临时重定向),一些旧客户端会错误地将请求方法转换为 GET
307(临时重定向) http1.1新增,请求方法和消息主体不会发生变化
304(未修改)无需再次传输请求的内容,可以使用客户端缓存的内容
401(未授权) 代表客户端错误,缺少身份验证凭证
403(禁止) 代表客户端错误,服务端拒绝授权访问
404(未找到) 服务器找不到请求的资源
500(服务器内部错误)
502(错误网关)作为网关或代理角色的服务器,从上游服务器(如tomcat、php-fpm)中接收到的响应是无效的
504(网关超时)扮演网关或者代理的服务器无法在规定的时间内获得想要的响应
http1.0 VS http1.1
缓存处理
1.0中主要使用header里的If-Modified-Since、Expires来做为缓存判断的标准
1.1则引入了更多的缓存控制策略例如ETag、If-Unmodified-Since、If-Match、If-None-Match等更多可供选择的缓存头来控制缓存策略
带宽优化及网络连接的使用
1.0中,存在一些浪费带宽的现象,例如客户端只需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能
1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接
错误通知的管理
1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
Host头处理
1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名
1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)
长连接
1.1支持长连接和请求的流水线处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在1.1中默认开启Connection: keep-alive
http2 VS http1
2是新的二进制格式解析,1是基于文本
多路复用,相同域名请求通过一个TCP连接并发完成(与keep-alive区别?)
Keep-Alive解决的核心问题是: 一定时间内,同一域名多次请求数据,只建立一次 HTTP 请求
Keep-Alive存在两个性能问题:串行的文件传输和同域并行请求限制带来的阻塞(6~8)个
http2中,有两个非常重要的概念,分别是帧(frame)和流(stream)。帧代表着最小的数据单位,每个帧会标识出该帧属于哪个流,流也就是多个帧组成的数据流
多路复用,代替原来的序列和阻塞机制,在一个 TCP 连接中可以存在多条流。也就是可以发送多个请求,然后通过帧中的标识区分请求。可以避免 HTTP 旧版本中的队头阻塞问题
每一帧都包含几个字段,有length、type、flags、stream identifier、frame playload等
header压缩(HPACK算法)
通讯双方各自cache
服务端推送
服务端推送
请求方式
GET vs POST
它们本质上就是TCP链接,但由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同(参数、幂等性)
GET产生一个TCP数据包;POST产生两个TCP数据包
GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据)
POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)
post表单Content-Type
application/x-www-form-urlencoded (发送前编码所有字符)
multipart/form-data (表单上传)
application/json (复杂数据结构,跨域会先发options)
text/xml (XML 作为编码方式的远程调用规范)
OPTIONS请求
预校验请求,用于获知服务端是否允许该跨域请求,触发条件:非简单跨域请求
简单请求
GET/HEAD/POST这3种方法之一
HEAD 方法请求资源的头部信息, 并且这些头部与 GET 方法请求时返回的一致
Content-Type: text/plain、multipart/form-data、application/x-www-form-urlencoded
没有使用 ReadableStream 对象
一个可读取的二进制流操作
XMLHttpRequestUpload 对象均没有注册任何事件监听器
9个首部安全字段:Accept、Accept-Language、Content-Language、Content-Type (需要注意额外的限制)、DPR、Downlink、Save-Data、Viewport-Width、Width
HTTPS
原理
先用非对称加密算法进行协商密钥生成与交换,后面再用协商密钥进行对称加密通信,SSL/TLS
SSL——Secure Sockets Layer
TLS是SSL升级版
对称加密:加、解密用的是同一串密钥,常见的算法有DES、AES
非对称加密:加、解密使用不同的密钥,一把作为公开的公钥,另一把作为私钥,常见的算法有RSA
SSL / TLS 握手
作用
商定双方通信所使用的的 TLS 版本 (例如 TLS1.0, 1.2, 1.3等等)
确定双方所要使用的密码组合
客户端通过服务器的公钥和数字证书上的数字签名验证服务端的身份
生成会话密钥,该密钥将用于握手结束后的对称加密
过程
客户端发送包含密码信息和随机字符串
服务端响应包含密码组合信息、随机字符串和证书
客户端验证证书并从证书里获取公钥,生成下一个随机字符串,并用公钥加密
客户端发送加密后的信息给服务端
服务端用私钥解密,获取信息
客户端和服务端使用相同算法,并使用收到是随机字符生成相同的密钥key,用于后面的对称加密
步骤
客户端:随机数+加密套件+SSL Version
服务端:随机数+确定的加密套件;证书下发;DH参数(如果是DH算法)
客户端:验证证书+生成密钥
模块化
commonJS
输出的是值的拷贝;运行时加载;加载的是一个脚本运行完才生成的对象
exports输出多个;module.exports只能输出一个
this 指向当前模块
AMD
require.js
CMD
sea.js
ES6
输出的值的引用;编译时输出;对外接口只是一种静态定义,解析阶段生成
export输出多个;export default只能输出一个
this 指向undefined
设计模式
创建型
工厂方法模式
相当于创建实例对象的new,提供一个创建对象的接口
应用场景:JQuery中的$、Vue.component异步组件、React.createElement等
单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点,一般登录、购物车等都是一个单例
应用场景:JQuery中的$、Vuex中的Store、Redux中的Store等
原型模式
结构型
适配器模式
用来解决两个接口不兼容问题,由一个对象来包装不兼容的对象,比如参数转换,允许直接访问
应用场景:Vue的computed、旧的JSON格式转换成新的格式等
装饰器模式
在不改变对象自身的基础上,动态的给某个对象添加新的功能,同时又不改变其接口
应用场景:ES7装饰器、Vuex中1.0版本混入Vue时,重写init方法、Vue中数组变异方法实现等
代理模式
为其他对象提供一种代理,便以控制对这个对象的访问,不能直接访问目标对象
应用场景:ES6 Proxy、Vuex中对于getters访问、图片预加载等
行为型
观察者模式
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知
发布/订阅模式
与观察者模式区别: 本质上的区别是调度的地方不同
观察者模式是由具体目标(发布者)调度的,而发布/订阅模式是统一由调度中心调的
所以观察者模式的订阅者与发布者之间是存在依赖的,而发布/订阅模式则不会
迭代器模式
供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示
应用场景: JQuery.each方法
状态模式
关键是区分事物内部的状态,事物内部状态往往会带来事物的行为改变,即允许对象在内部状态发生改变时改变它的行为
应用场景:灯泡状态、红绿灯切换等
设计六大原则
单一职责原则
里氏替换原则
依赖倒转原则
接口隔离原则
最少知识原则(迪米特法则)
开放封闭原则
单元测试
Jest
特性
Facebook出品
内置强大的断言与 mock 功能
内置测试覆盖率统计功能
内置 Snapshot 机制
API
mock函数:jest.fn()
对象方法监听:jest.spyOn
expect(value)
.toBe(value)
浮点数的比较:.toBeCloseTo
对象的深比较:.toEqual(value)
Mocha
工程化
解决了什么问题
资源处理:合并和压缩css/js、合并雪碧图、Base64自动转换、webp图片转换、增加MD5后缀
页面结构规范化(将样式表放在顶部,将脚本放在底部,尽早刷新文档的输出)
代码自动化校验(Eslint)
作用:避免错误、最佳实践代码、变量规范和格式规范、使用新的语法、找出冗余代码等
配置文件 .eslintrc.js
原理:使用 espree 把 JS 语法转换成抽象语法树ATS,然后通过 AST selectors找到静态代码中的内容,再根据rule的规则去判断这一段js是否符合eslint的规范(每一个规则都是一个插件)
更好、更高效的书写体验,比如css/js预处理语言使用、ES6+等新技术的体验
其它:模板解析可以在编译阶段处理,提高性能
编译类型
Just-in-time (JIT),即时编译
Ahead Of Time (AOT),提前编译
如何实现覆盖式打包和非覆盖式打包
覆盖式:herf="foo.css?v=wekdsl923"
非覆盖式:herf="foo.wekdsl923.css"
用户无感知,可以平滑升级
缓存管理精确到文件
可以设置超长的缓存时间
还可以支持灰度更新
Base64
编码
一种将二进制数据转成文本数据的方案。对于非二进制数据,是先将其转换成二进制形式,然后每连续6比特(2的6次方=64)计算其十进制值,根据该值在A--Z,a--z,0--9,+,/ 这64个字符中找到对应的字符,最终得到一个文本字符串
Base64把3个8位字节(3*8=24)转化为4个6位的字节(4*6=24),之后在6位的前面补两个0,形成8位一个字节的形式。 如果剩下的字符不足3个字节,则用0填充,输出字符使用'=',因此编码后输出的文本末尾可能会出现1或2个'='
Base64转换后比原有的字符串长1/3
解码
将4个字节转换成3个字节,先读入4个6位(用或运算),每次左移6位,再右移3次,每次8位,这样就还原了
优点
减少http请求次数
支持广泛,平台和语言支持都比较广泛
缺点
无法直接缓存,只能缓存包含 base64 的文件
他的编码方式注定会有三分之一的冗余
如果需要通过get请求传输,需要再做一层转换(在url中+号会被转义,在BASE64的基础上进行一下的编码:去除尾部的"=",把"+"替换成"-",把"/"替换成"_")
webpack
优化
使用文件指纹进行版本控制和缓存
tree shaking
code split
DLL
多进程打包(thread-loader)
npm/yarn
痛点
依赖结构的不确定性
扁平化算法本身的复杂性很高,耗时较长
项目中仍然可以非法访问没有声明过依赖的包
pnpm
速度快
基于内容寻址,高校利用磁盘空间
支持monorepo
安全性高
微前端
一种利用微件拆分来达到工程拆分治理的方案,可以解决工程膨胀、开发维护困难等问题
方案
NPM式:子工程以NPM包的形式发布源码;打包构建发布还是由基座工程管理,打包时集成
iframe式:子工程可以使用不同技术栈;子工程之间完全独立,无任何依赖;基座工程和子工程需要建立通信机制;无单页应用体验;路由地址管理困难
通用中心路由基座式:子工程可以使用不同技术栈;子工程之间完全独立,无任何依赖;统一由基座工程进行管理,按照DOM节点的注册、挂载、卸载来完成
特定中心路由基座式:子业务线之间使用相同技术栈;基座工程和子工程可以单独开发单独部署;子工程有能力复用基座工程的公共基建
MVVM框架
Vue.js
生命周期
beforeCreate
在实例初始化之前,new vue()后执行的第一个勾子,当前阶段data、methods、computed、watch上的数据和方法都不能访问
created
在实例创建完成后被立即调用。当前阶段完成了数据观测,可使用和更改数据(不会触发updated函数),但无法与DOM做交互如果非要想,可以通过vm.$nextTick来访问Dom
beforeMount
发生在挂载之前,在这之前template模板已导入渲染函数编译。而当前阶段虚拟Dom已经创建完成,即将开始渲染。在此时也可以对数据进行更改,不会触发updated。
mounted
在挂载完成后发生,在当前阶段,真实的Dom挂载完毕,数据完成双向绑定,可以访问到Dom节点,使用$refs属性对Dom进行操作。
beforeUpdate
发生在更新之前,也就是响应式数据发生更新,虚拟dom重新渲染之前被触发,你可以在当前阶段进行更改数据,不会造成重渲染。
updated
发生在更新完成之后,当前阶段组件Dom已完成更新。要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新。
beforeDestroy
发生在实例销毁之前,在当前阶段实例完全可以被使用,我们可以在这时进行善后收尾工作,比如清除计时器
可以:内存释放
destroyed
发生在实例销毁之后,这个时候只剩下了dom空壳。组件已被拆解,数据绑定被卸除,监听被移出,子实例也统统被销毁。
activated
被 keep-alive 缓存的组件激活时调用
deactivated
被 keep-alive 缓存的组件停用时调用
响应式原理
采用数据劫持结合发布-订阅模式,通过 Object.defineproperty 来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发响应的监听回调
核心实现类
Observe
Dep
Watcher
组件间通信
prop-$emit
props篡改:如果props是基础数据类型,当改变的时候,就会报错;当是引用数据类型时,子组件可以修改
跨层级通信,兄弟组件通讯困难
this.$children-this.$parent
this.$children不可控性大,有一定风险
不利于组件化
兄弟组件深层次嵌套组件通讯困难
provide-inject
不适合兄弟通信
父级组件无法主动通信
vuex
流程相比稍微复杂
EventBus
维护困难,容易引起连锁问题
需要谨小慎微的命名规范
不利于组件化开发
事件总线二 new Vue
和EventBus一样的缺点
3.x
proxy的使用
可直接监听数组类型的数据变化
监听的目标为对象本身,不需要像Object.defineProperty一样遍历每个属性,有一定的性能提升
可拦截apply、ownKeys、has等13种方法,而Object.defineProperty不行
直接实现对象属性的新增/删除
架构遗漏问题
typescript替换flow
更快更小
tree shaking
将没有使用的模块摇掉,这样来达到删除无用代码的目的
新增Composition API,更好的逻辑复用和代码组织
template和jsx的区别
template在结构上更符合视图与逻辑分离的习惯,简单直观好维护,最后需要webpack这些构建工具预编译成render函数
jsx是手写render函数的语法糖,具有更高的灵活性,在复杂的组件中更具有优势
diff算法
对比节点本身,判断是否是同一节点,如果不是则删除该节点,重新创建节点进行替换
如果是相同节点,进行patchVnode,判断如何对子节点进行处理,先判断一方有子节点,一方没有子节点的情况(如新的children没有子节点,把旧的子节点移除)
比较结果,如都有子节点,则进行updateChildren,判断如何对这些新老节点的子节点进行操作
匹配时,找到相同的子节点,递归比较子节点
diff时,只对同层的子节点进行比较,放弃跨级的节点比较,时间复杂度为O(n)
React
生命周期
constructor
用于初始化内部状态,唯一可以直接修改state的地方(this.state.xx = xxx)
getDeriveStateFromPros
v16.3 新引入
当state需要从props初始化
每次render都会调用
场景:表单控件获取默认值
不推荐使用:维护两者状态一致性会增加复杂度
componentDIdMount
UI渲染完后调用,只执行一次
场景:ajax获取外部资源
componentDidUpdate
每次UI更新都会调用
场景:需要根据props变化来重新获取数据
shouldComponentUpdate
决定Virtual Dom是否要更新,一般可以由pureComponent自动实现
场景:性能优化
componentWillUnmount
组件移除时被调用
场景:资源释放
getSnapshotBeforeUpdate
v16.3 新引入
在render之前调用,state已更新
场景:获取render之前的DOM状态
diff算法
广度优先的分层比较(时间复杂度O(n))
前提假设:组件DOM结构层级相对稳定的,类型相同的兄弟节点可以被唯一标识
设计模式
高阶组件
接受组件为参数,返回新的组件
函数子组件
把一个函数作为children
小程序
生命周期
App
onLaunch
小程序初始化
onShow
小程序启动或切前台
onHide
小程序切后台
onError
发生错误
页面
onLoad
页面加载
onShow
启动或切前台
onReady
页面初次渲染完成
onHide
切后台
onUnload
页面卸载
组件
created
在组件实例刚刚被创建时执行
attached
在组件实例进入页面节点树时执行
ready
在组件在视图层布局完成后执行
moved
在组件实例被移动到节点树另一个位置时执行
detached
在组件实例被从页面节点树移除时执行
error
每当组件方法抛出错误时执行
性能优化
控制包大小(分包+代码压缩+及时清理无用的代码+减少资源包中的图片等资源的数量和大小)
setData的优化(调用频率、数据量、抛弃无关界面渲染的数据、切勿在后台页面进行、数据局部更新)
利用缓存
事件优化(去掉不必要的事件绑定、不要在节点的data前缀属性中放置过大的数据)
避免不当的使用onPageScroll
setData背后发生了什么
视图层目前使用 WebView 作为渲染载体,而逻辑层是由独立的 JavascriptCore 作为运行环境。
在架构上,WebView 和 JavascriptCore 都是独立的模块,并不具备数据直接共享的通道。
通过两边提供的 evaluateJavascript 所实现。即用户传输的数据,需要将其转换为字符串形式传递,同时把转换后的数据内容拼接成一份 JS 脚本,再通过执行 JS 脚本的形式传递到两边独立环境。
evaluateJavascript 的执行会受很多方面(哪些方面?)的影响,数据到达视图层并不是实时的
computed vs watch
watch 的性能比 computed 更好;但 computed 的用法更简洁干净
computed 字段状态只能依赖于 data 和其他 computed 字段,不能访问 this
如果不可避免要访问 this ,则必须使用 watch 代替
watch vs observers
flutter
Dart
常用数据类型
Numbers
int
double
Strings
Booleans
List
Maps
特性
属于是强类型语言
if 等语句只支持 bool 类型,switch 支持 String 类型
number 类型分为 int 和 double ,没有 float 类型
~/:取整运算符
a??=b : 当a不存在是,使用b的值
为什么选Dart
开发效率高
基于JIT(Just-in-time 即“即时编译”)的快速开发周期
基于AOT(Ahead of time 即 “提前编译”)的发布包
高性能(Dart支持AOT,在这一点上可以做的比JavaScript更好)
快速内存分配
类型安全
widget
StatelessWidget
用于不需要维护状态的场景,它通常在build方法中通过嵌套其它Widget来构建UI,在构建过程中会递归的构建其嵌套的Widget
原理
UI是直接通过 skia 渲染的 ,而 React Native 是将 js 中的控件转化为原生控件,通过原生去渲染的
生命周期
initState
表示当前 State 将和一个 BuildContext 产生关联,但是此时BuildContext 没有完全装载完成
didChangeDependencies
当 State 对象的依赖关系发生变化时,该方法被调用,初始化时也会调用
deactivate
当 State 被暂时从视图树中移除时,会调用这个方法,同时页面切换时,也会调用
dispose
Widget 销毁了,在调用这个方法之前,总会先调用 deactivate()
didUpdateWidge
当 widget 状态发生变化时,会调用
Node.js
用nvm进行版本管理
如何利用多核CPU以及创建集群
cluster模块
PM2工具
通过配置文件启动:pm2 start/restart pm2.json
重启指定的进程: pm2 stop/restart 0
显示所有进程状态:pm2 list
启动多核CPU:pm2 start test.js -i 2
如何支持https
openssl生成公钥私钥
基于 express的 https模块 实现,设置options配置, options有两个选项,一个是证书本体,一个是密码
内存泄漏怎么检测
通过内存快照,可以使用node-heapdump获得内存快照进行对比,查找内存溢出
可视化内存泄漏检查工具 Easy-Monitor
两个node程序之间怎样交互
通过fork,原理是子程序用process.on来监听父程序的消息,用 process.send给父程序发消息,父程序里用child.on,child.send进行交互
构架:应用app --> V8及node内置架构 --> 操作系统
内置架构: 核心模块(javascript实现) --> c++绑定 --> libuv + CAes + http
核心模块:EventEmitter, Stream, FS, Net和全局对象
全局对象:process, console, Buffer
Docker
算法与数据结构
方法论
切碎知识点(庖丁解牛)
刻意练习
练习缺陷、弱点的地方
反馈
主动型(高手的代码:github、leetcode)
被动型(高手给你指点)
面试切题
看懂题意
所有的解,比较时间和空间复杂度
写代码
数据类型
Stack
Queue
Set
Hash Set
Tree Set
Map
Hash Map
Tree Map
必备算法
快排
树的遍历
深度优先
先序(DLR)
中序(LDR)
后序(LRD)
广度优先
常考题目
数组相关
数组交集,编写一个函数,输入两个数组,输出它们的交集。输出数组中不含重复的元素,元素排列顺序可随意。
三角形个数,输入一个非负整数的数组,如果将数组元素选作三角形的边长,编写一个函数,输出这个数组可构成的三角形数量。
数组切分问题,输入一个正序排列的整型数组,如果它可以被切分为1个或多个子序列,输出True,反之False。子序列需为连续的整型数组,并且长度至少为3。
top k
二叉树相关
二叉树的搜索,输入一个普通二叉树的根节点,实现一个调度器,调用调度器的next()方法,将返回二叉树中下一个最小的数;调用迭代器的hasNext()方法,将返回是否存在下一个数。二叉树节点是整数,无序。
参考文档
ECMA-262
0 条评论
下一页