- 手写:深拷贝、promise、bind/call
# 1、var let const 的区别
变量提升(js预解析)、window对象、块级作用域
# 3、模块化
- AMD/CMD:require.js define定义。AMD是提前执行,CMD是延迟执行
- CommonJs:Node.js require导入,module.exports导出,
- (不能直接给exports赋值来导出,因为复杂数据类型赋值指针的问题,module.exports数据不会跟着改变)。
- es module:原生es6,import导入,export 或 export default导出
- 和commonjs的区别:commonjs支持动态导入;commonjs是异步导入;commonjs导出的是值的拷贝(浅拷贝),es module导出的是值的真实引用地址。
# 4、vue数据响应式原理
https://cn.vuejs.org/v2/guide/reactivity.html
- Object.defineProperty()是针对对象的特定属性通过设置get set方法来劫持数据,不兼容ie9以下版本。
- Proxy是针对object类型数据的所有属性都添加get set方法进行监听,在实现嵌套数据的遍历时更简单,性能更好,而且原生支持数组类型的监听,是es6新增的api,不兼容ie。
- 在组件挂载的时候解析模板,使用到响应式数据的地方会生成watcher,解析求值时会触发响应式数据里的get方法,通过dep.addSubs()将watcher添加到subs数组里收集依赖;
- 在更改数据时会触发set方法,通过dep.notify(),遍历subs数组的所有watcher,调用update()派发更新
- 然后执行render函数,对比生成VDom,最后映射为真实dom展示出来。
# 5、数组的reduce方法
- reduce((start, current, index, origin) => { return newRes }, initVal)
- 先看有没有设初始值initVal, 如果没有initVal,那么就从数组第二项开始遍历,累加值start的初始值取数组第一项; 如果有initVal,那么就从数组第一项开始遍历,累加值start的初始值就取initVal; 累加值start后面循环时取的是回调函数的返回值newRes; current是当前遍历的数组元素,index是当前数组元素的索引,origin是原始数组。
# 6、Generator函数
在定义的函数名前加一个星号*,内部通过yield设置分段点,返回一个迭代器,迭代器调用next()方法来使函数分段执行。
可以用来处理ajax请求,解决回到地狱问题,react的状态管理库dva也是推荐这样来处理请求。
function *fetch() { yield ajax(url, () => {}) yield ajax(url1, () => {}) } let it = fetch() let result1 = it.next() let result2 = it.next()
1
2
3
4
5
6
7
# 7、promise
- 三种状态:已完成resolved、已拒绝rejected、等待中pending。
- then的回调函数的返回值会被Promise.resolve包装成promise对象。
- Promise.all()所有的都成功或有一个失败;Promise.race()有一个成功或所有都失败。
# 8、async和await
async也是讲函数的返回值用Promise.resolve()包裹一下,和await配套使用, 本质是promise和generator的语法糖。
# 9、requestAnimationFrame
- window.requestAnimationFrame(callback) 在下一次浏览器重绘之前执行,一般屏幕是1秒60帧,执行间隔就是1000/60,约等于16.67ms。
- 能用于精确的控制执行间隔,也用于保持动画的流畅性。
# 10、event loop事件循环和事件队列
先在执行栈里执行同步代码,再查找异步队列,推入执行栈里执行异步代码,再执行下一个异步队列,这个过程就形成了事件循环,在一次事件循环中先执行微任务(promise)再执行宏任务(setTimeout)。
# 11、浏览器缓存
- 强缓存:Cache-Control > Expires
- 协商缓存:ETag > Last-Modified
# 12、安全
XSS:input输入
<script>alert(1)</script>
,录入数据库中,然后再数据回显的时候可能会执行script脚本。- 防范:对输入的尖括号<>进行检查校验。
CSRF:通过img标签src指向一个接口地址,来恶意伪造用户的请求。
- 防范:请求时附带token之类的验证信息;阻止第三方网站的请求(cors或referer白名单)。
# 13、网页性能优化
- cdn服务加速网络访问
- 浏览器缓存
- gzip压缩
- 图片优化:压缩、base64、svg、字体图标、懒加载
- 首屏优化:路由懒加载、图片懒加载、dom懒加载、loading效果、骨架屏
- dns预解析(dns-prefetch)、js和css的预解析preload(当前页资源)和prefetch(其他页资源)
- script标签放底部,适当地加async和defer异步属性
- 减少重排重绘
- 节流和防抖
- splitChunks分离优化
# 14、MVVM
- mvc:model、view、controller,controller同时控制model和view,工作职责太大了,不利于频繁更新的场景。
- mvvm:model、view、viewModel,viewModel只控制操作model数据,然后通知view层更新视图,效率更高。
# 15、虚拟dom和diff算法
- 用js对象描述dom,只更新修改的部分,提升渲染性能,虚拟dom的概念也有利于跨端开发。
- diff算法:
- dom有很多子节点,虚拟dom对象也有很多层级,只对同层进行比较,从最外层节点开始,
- 首先判断两个节点的key值,如果key不一致,就认为是两个不同的节点,算法结束,
- 如果key相同或没有key值,就判断其他属性是否相同,如果不相同就认为是两个节点,如果相同就判断子节点,这样依次递归查找。
# 18、Vue.extend()
用于拓展组件生成一个构造器,通过new构造器生成一个新的组件实例,类似组件的继承,vue3.0里废弃了。
# 19、mixins
合并规则:data数据和methods之类的对象类型的值会遍历合并,冲突时以组件内优先;钩子函数合并时都会执行,先执行mixins里的。
# 21、vue模板编译过程
将模板编译为抽象语法树AST(多叉树) 将AST转换为渲染函数 执行渲染函数生成VDOM 将VDOM映射为真实DOM
# 22、nextTick()原理
- vue的dom更新是异步执行的,本身也是使用的nextTick()。修改数据时,会开启一个异步队列,将watcher的update()更新操作缓冲在同一事件循环中,同一个watcher只会被推入队列一次(通过watcherId去重),然后在下一事件循环中刷新队列,执行代码。这样做能减少频繁不必要的更新,提升更新渲染的效率。
- nextTick()内部通过一系列异步api尝试将回调函数放入异步队列里,包括promise、mutationObserver、setImmediate,最后是setTimeout。
- 所有的回调方法都会放入callbacks数组里,最后通过flushCallbacks()方法遍历callbacks数组来依次执行回调。
# 23、setState
setState不能保证同步执行,
在钩子函数和合成函数里表现的是异步,在setTimeout和原生事件里表现的是同步。
# 24、UDP和TCP
UDP更轻便,不需要经历建立连接和断开连接的阶段,适合直播。
TCP三次握手:
- 客户端向服务端发送syn请求包,询问服务端能不能收到请求
- 服务端接收后向客户端发送ack确认包,告诉客户端能收到
- 客户端向服务端发送ack确认包,告诉服务端我也能收到,然后客户端进入准备状态,服务器接收后也进入准备状态。
TCP四次挥手:
- 客户端向服务端发送fin请求包,告诉服务端要断开连接
- 服务端收到后发送ack确认包告诉客户端收到了断开请求
- 服务端有未传完的数据会继续传完,然后向客户端发送fin请求包,告诉客户端可以断开连接了
- 客户端收到后向服务端发送ack确认包,然后客户单断开连接,服务端收到后也断开连接
# 25、http、https、http2
请求报文
请求行:请求方法、请求地址、请求协议
请求头:content-type、referer、user-agent
请求体:body数据
状态码:
301永久重定向 302临时重定向 304从缓存中读取
400请求报文错误 403服务器拒绝访问 404找不到资源 414请求数据超限
https
https是通过tls对数据进行了加密,使用了对称加密(AES)和非对称加密(RSA)
http2
http1同时只能请求6个连接,而http2实现了多路复用,可以复用一个tcp连接。
http1通过文本方式传输数据,http2通过二进制传输。
https2对header进行了压缩。
http2可以由服务端push推送。
# 26、输入 URL 到页面渲染的整个流程
dns解析 域名-ip com一级域名、baidu二级域名、www三级域名
tcp连接 三次握手、keep-alive四次挥手
nginx 负载均衡
状态码 200 40x 50x 30x
强缓存、协商缓存
gzip 自动按编码格式解析
html解析 异步script
dom树、css树、render树、重排重绘
gpu绘制,展示完成
# 27、设计模式
- 工厂模式
- 通过new出来一个实例对象来实现,可以new一个构造函数,也可以new一个class类。
- 作用就是隐藏了创建实例的复杂过程,用户不用关心怎么实现的,只需提供一个接口,让用户调用,传入配置生成实例即可。
- 单例模式
- 单例模式就是保证全局只有这一个对象,这样可以共用同一个对象的数据
- 定义一个对象,返回或者导出一个函数,在函数里判断这个定义的对象是否已存在,不存在就创建,已存在就直接返回
- 适配器模式
- 用来解决两个接口不兼容的情况,不改变已有的接口,而是包装处理一层来兼容其他接口。
- 就像电脑不能读取sd卡,可以通过读卡器来识别读取sd卡。
- 装饰模式
- 不改变已有的接口,而是给接口添加功能。
- 改已有的接口有风险,容易产生未知的bug,而利用装饰模式,只需要扩展功能,比较安全,也方便维护。
- 代理模式
- 是为了控制对对象的访问,不让外部直接访问对象。
- 比如js中的事件代理,列表多个元素,都绑定点击事件不方便,可以给父元素绑定事件,通过事件对象获取到子元素来处理。
- 观察者模式
- 通过一对一或者一对多的关系,当对象改变时,通知订阅方。
- vue中的数据响应式也是使用的观察者模式,通过对象代理,在get方法里收集依赖,对象改变时触发set方法,然后派发更新,就实现了数据的响应式。
# 28、数据结构
时间复杂度
- 使用最差的时间来衡量一个算法的好坏,这种衡量方式就是时间复杂度
栈
- 线性结构,在同一端进行添加和删除的操作,先进后出
队列
- 线性结构,在一端添加,另一端删除,先进先出
链表
- 线性的递归结构,通过指针相连,
- 可以用git仓库的存储理解,不同的分支和节点连接在一起。
二叉树
- 递归结构,有一个根节点,每个节点最多有两个子节点。
二分搜索树
- 也是二叉树,特点是右节点比左节点的值大,用于搜索查找比较高效。
堆
- 非线性的数据结构,可以看成是完全二叉树组成的数组对象。
- 完全二叉树:除最深层外的其他层的每个节点的子节点都是满的,而且最深层的节点是仅靠着最左侧排列的。
# 29、排序算法
冒泡排序:谁大谁就交换位置,一直找出最大的放最右边,剩余的再重复。时间复杂度:O(n * n)
插入排序:从二个开始依次取出往前插入,插入方式就是比较大小,插入对应的位置,这样前面已插入的都是排序好的了,重复执行。时间复杂度:O(n * n)
快速排序:分治法,选择一个数据作为基准值,小鱼基准值的放左边,大于基准值的放右边,然后拆成两部分,每部分再重复步骤,最后拆到最小粒度。时间复杂度:平均是O(n * logN),最坏是O(n * n)
# 30、flex: 1
全写 flex: 1 1 0%;
flex-grow 定义flex的放大系数,即有剩余空间时放大多少,默认值1。
flex-shrink 定义flex的缩小系数,即空间不足时缩小多少,默认值1。
flex-basis 定义放大或缩小的计算尺寸,根据这个尺寸来判断空间够不够,默认值auto。
# 31、安全区适配
viewport-fit: cover
constant(safe-area-inset-bottom)
env(safe-area-inset-bottom)
# 32、webpack优化
chunk分离 splitChunks
js压缩 terser-webpack-plugin
css压缩 optimize-css-assets-webpack-plugin
gzip压缩
css分离 mini-css-extract-plugin
tree-shaking es6模块化导入导出支持静态分析
DllPlugin 创建manifest.json文件记录缓存信息
IgnorePlugin 忽略打包指定文件
# 33、继承实现
class Chilid extends Parent { constructor () { super() } }
1
2
3
4
5function Parent () { this.name = 'p' } function Child () { Parent.call(this) this.age = 27 } Child.prototype = new Parent()
1
2
3
4
5
6
7
8
# 34、new 操作符做了什么
- 创建了一个空对象
- 通过this变量指代该对象
- 给对象的属性赋值,并继承了构造函数的原型
- 构造函数隐式地返回this
# 35、面向对象
运用类、继承、封装等进行程序设计的一种思想。
- 能够将复杂问题和复杂逻辑简单化
- 方便扩展
- 方便维护
# 36、node
- 特点:js运行环境、依赖v8引擎解释代码、事件驱动、非阻塞I/O、单进程和单线程
- 优点:高并发、文件读写效率高
- 缺点:只支持单核cpu、可靠性低(代码出错容易导致整个系统崩溃)
# 37、set map
都是集合类型的数据结构
- set 数据项不重复
- weakSet 数据项都是对象,且是弱引用的对象,随时可能会作为垃圾被回收,所以无法遍历
- map 键值对集合,键可以是任意类型
- weakMap 键只能是对象,该对象也是弱引用,无法遍历
# 38、ts泛型
不确定数据类型,又想对类型有一定的约束。
比如函数入参和返回值保持一致的类型。
泛型进一步约束:
interface Lengthwise {
length: number;
}
function say<T extends Lengthwise>(arg: T): T {
console.log(arg.length)
return arg;
}
2
3
4
5
6
7
8
# 39、for...in 和 for...of
for...in
用于迭代对象的除Symbol以外的可枚举属性
迭代数组时,迭代的是数组的下标
for...of
用于迭代对象的数据
迭代数组时,迭代的是数组的项
Array.prototype.arrCustom = function() {}; let iterable = [3, 5, 7]; iterable.foo = 'hello'; for (let i in iterable) { console.log(i); // 0, 1, 2, "foo", "arrCustom" } for (let i of iterable) { console.log(i); // 3, 5, 7 }
1
2
3
4
5
6
7
8
9
10
11
12
# 40、水平垂直居中
- flex
- absolute
- table-cell 父盒子设置display: table-cell; 子盒子设置display: inline-block;
- grid 父盒子设置display: grid; place-items: center center;
# 41、push、pop、unshift、shift
- push()和unshift()
- 向数组的 尾部/头部 *添加若干元素*,并返回 数组的 新长度;
- pop()和shift()
- 从数组的 尾部/头部 *删除1个元素(删且只删除1个)*,并返回 被删除的元素;空数组是继续删除,不报错,但返回undefined;
# 42、vue重写数组的7个方法
push pop unshift shift splice sort reverse
- 通过Object.create()对Array的原型创建一个实例对象(arrayMethods)
- 遍历这7个方法,对创建的这个实例对象以及相应的方法属性添加响应式,进行监听
- 重写这些方法,在调用原方法后,再手动通知订阅者(ob.dep.notify())来派发更新
- https://www.cnblogs.com/ming1025/p/13082822.html
# 43、keep-alive原理
通过缓存vnode实现,在渲染的时候对比组件的name(通过include和 exclude控制),命中缓存时就从缓存中读取。
activated deactivated
内存超限时通过LRU算法来管理
# 44、css modules
在css-loader里配置
{
loader: 'css-loader',
options: {
modules: {
localIdentName: "[name]_[local]__[hash:5]"
}
},
}
2
3
4
5
6
7
8
# 45、addEventListener
options参数
- 布尔值,默认false,冒泡阶段触发
- 对象值,
- capture,默认false,冒泡阶段触发
- once,默认false,是否只调用一次
- passive,chrome等浏览器下默认是true,是否不调用preventDefault(),提升scroll事件和touch事件(touchstart touchmove)的性能
# 46、小程序运行机制
小程序是运行在宿主环境,也就是app客户端内,
传统的网页是js执行和dom渲染在一个线程里,js会阻塞dom的渲染,
而小程序是分成了渲染层和逻辑层,在不同的线程里,渲染层运行在webview里,逻辑层运行在JsCore里,js的运行不会阻塞渲染,提升了页面运行效率。
渲染层和逻辑层之间是通过native客户端来进行数据交互,也就是使用的小程序基础库。
在逻辑层里通过setData改变数据,native转发到渲染层,然后对比数据生成虚拟dom,最后更新在视图上。
性能优化:减少setData的频率、减小setData传递的数据量。
# 47、手机调试
- vconsole
- charles
- spy-debugger
- chrome devtools (只能安卓,需要开启开发者调试和usb调试、连接数据线、安装usb驱动)
# 48、render函数
render函数可以用来动态创建组件节点:
参数第一个是createElement函数,通常用h指代,参数第二个是context对象,一般在函数式组件(无状态,渲染快)里使用。
createElement函数参数:标签名,标签属性对象,子节点数组。返回值是VNode,所有VNode组成VDom。
render函数可以用jsx语法代替:
- 先安装插件babel-preset-jsx,在babel.config.js里配置引入,然后就可以在jsx后缀的文件里使用了。
- 比render函数更简单直观。
# 49、native交互
通过url scheme拦截
- 客户端注册一个url scheme协议,h5使用并传递参数,客户端拦截获取到数据
- 不适合数据量大的;只能由h5向客户端传递数据
通过在window对象上添加bridge桥接属性
- 客户端引入相应的库可以执行js代码(evaluateJavascript)
- 通过在window对象上约定一个属性,作为桥接,所有的交互方法都注册在这个桥接上
- 比如,h5向客户端传递数据,就是客户端提前注册一个方法,h5这边调用并传递数据,必要时多设置一个参数作为回调函数,h5提前注册好回调函数,客户端接收参数后先执行完正常的逻辑,最后调用回调参数即可。
推荐插件:DSBridge,支持同步调用,兼容性好。
# 50、webview优化
- 提前初始化webview
- app启动时在后台初始化一个全局的webview,后续打开页面时就使用这个webview
- 客户端代理数据请求
- 必要的时候可以由客户端在初始化webview时就获取接口数据,页面初始化后从客户端里拿数据。(例如动态图片域名)
- 利用dns缓存
- webview页面域名使用和客户端api相同的域名,域名解析时会缓存dns。