原生js
描述js的事件循环机制
任务进入执行栈之后会判断一下是否是同步任务,若是同步任务就会进入主线程执行;异步任务就会到事件表里面注册回调函数到事件队列。
- 同步和异步任务分别进入不同的执行”场所”,同步的进入主线程,异步的进入Event Table并注册函数
- 当指定的事情完成时,Event Table会将这个函数移入Event Queue
- 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
- 上述过程会不断重复,也就是常说的Event Loop(事件循环)
- 宏任务:整体代码script、setTimeout、setInterval、setImmediate, I/O, UI rendering。
- 微任务:原生Promise中then方法、process.nextTick、MutationObserver, Object.observe
什么是闭包,为什么有闭包概念,存在什么问题,适用什么应用场景
闭包就是能够读取其他函数内部变量的函数
- 函数嵌套函数
- 函数内部可以引用函数外部的参数和变量
- 参数和变量不会被垃圾回收机制回收
浏览器渲染
手动实现浅拷贝、深拷贝
// 基础版
function clone(target) {
let cloneTarget = {}
for (const key in target) {
cloneTarget[key] = target[key]
}
return cloneTarget
}
// 只考虑{}
function deepClone(target) {
if (typeof target === 'object') {
let cloneTarget = {}
for (const key in target) {
cloneTarget[key] = deepClone(target[key])
}
return cloneTarget
} else {
return target
}
}
手动实现防抖节流函数
// 防抖
// keyup事件、resize scroll
function debounce(fn, delay, scope) {
let timer = null;
// 返回函数对debounce作用域形成闭包
return function () {
// setTimeout()中用到函数环境总是window,故需要当前环境的副本;
let context = scope || this, args = arguments;
// 如果事件被触发,清除timer并重新开始计时
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(context, args);
}, delay);
}
}
// 节流
function throttle(fn, threshold, scope) {
let timer;
let prev = Date.now();
return function () {
let context = scope || this, args = arguments;
let now = Date.now();
if (now - prev > threshold) {
prev = now;
fn.apply(context, args);
}
}
}
function throttle2(fn, threshold, scope) {
let timer;
return function () {
let context = scope || this, args = arguments;
if (!timer) {
timer = setTimeout(function () {
fn.apply(context, args);
timer = null;
}, threshold)
}
}
}
new 操作符过程发生了什么
- 创建一个空对象
- 设置原型链。把构造函数的prototype属性 作为空对象的原型(同时也有了prototype的方法,例如this.age 就是用到了prototype的this这个方法)
- 改变this指向。this赋值给这个空对象,执行构造函数函数,完成赋值
- 如果函数没有返回值 就返回这个this对象
为什么会有跨域、怎么解决
图片压缩原理
函数参数为外部的一个对象,函数内部删除对象的某个属性,元数据会不会改变
继承(手写class继承)
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
sayHello(name) {
console.log(`hello ${name}`)
}
}
class men extends Person {
constuctor(name, age) {
super(name, age)
}
}
const xiaohong = new Person('小红', 18)
xiaohong.sayHello() // hello 小红
const xiaogang = new men('小刚', 20)
xiaogang.sayHello()
Promise简述
vue
Vue双向绑定、router原理
nextTick原理,除了Mutation Observer还有什么实现方式
keepalive如何实现刷新
computed实现原理
created和mounted的区别
react
react的setState是同步异步?为什么
react里性能优化在哪个生命周期函数?
shouldComponentUpdate 这个方法用来判断是否需要调用 render 方法重新描绘 dom。因为 dom 的描绘非常消耗性能,如果我们能在 shouldComponentUpdate 方法中能够写出更优化的 dom diff 算法,可以极大的提高性能。
什么是高阶组件(hoc)
高阶组件[higher order component]是一个以组件为参数并返回一个新组件的函数。HOC 运行你重用代码、逻辑和引导抽象。最常见的可能是 Redux 的 connect 函数。除了简单分享工具库和简单的组合,HOC 最好的方式是共享 React 组件之间的行为。如果你发现你在不同的地方写了大量代码来做同一件事时,就应该考虑将代码重构为可重用的 HOC。
react生命周期,各生命周期作用
componentWillMount() – 在渲染之前执行,在客户端和服务器端都会执行。
componentDidMount() – 仅在第一次渲染后在客户端执行。
componentWillReceiveProps() – 当从父类接收到 props 并且在调用另一个渲染器之前调用。
shouldComponentUpdate() – 根据特定条件返回 true 或 false。如果你希望更新组件,请返回true 否则返回 false。默认情况下,它返回 false。
componentWillUpdate() – 在 DOM 中进行渲染之前调用。
componentDidUpdate() – 在渲染发生后立即调用。
componentWillUnmount() – 从 DOM 卸载组件后调用。用于清理内存空间。
16.8+
1. 挂载阶段: constructor(props): 实例化。
static getDerivedStateFromProps 从 props 中获取 state。
render 渲染。
componentDidMount: 完成挂载。
2. 更新阶段: static getDerivedStateFromProps 从 props 中获取 state。
shouldComponentUpdate 判断是否需要重绘。
render 渲染。
getSnapshotBeforeUpdate 获取快照。
componentDidUpdate 渲染完成后回调。
3. 卸载阶段: componentWillUnmount 即将卸载。
4. 错误处理: static getDerivedStateFromError 从错误中获取 state。
componentDidCatch 捕获错误并进行处理。
简述redux
// 三个原则
// 单一事实来源:整个应用的状态存储在单个 store 中的对象/状态树里。单一状态树可以更容易地跟踪随时间的变化,并调试或检查应用程序。
// 状态是只读的:改变状态的唯一方法是去触发一个动作。动作是描述变化的普通 JS 对象。就像 state 是数据的最小表示一样,该操作是对数据更改的最小表示。
// 使用纯函数进行更改:为了指定状态树如何通过操作进行转换,你需要纯函数。纯函数是那些返回值仅取决于其参数值的函数。
// redux的组件
// Action – 这是一个用来描述发生了什么事情的对象。
// Reducer – 这是一个确定状态将如何变化的地方。
// Store – 整个程序的状态/对象树保存在Store中。
// View – 只显示 Store 提供的数据。
// 定义action
// React 中的 Action 必须具有 type 属性,该属性指示正在执行的 ACTION 的类型。必须将它们定义为字符串常量,并且还可以向其添加更多的属性。在 Redux 中,action 被名为 Action Creators 的函数所创建。以下是 Action 和Action Creator 的示例:
function addTodo(text) {
return {
type: ADD_TODO,
text
}
}
// 简述
// redux 是一个应用数据流框架,主要是解决了组件间状态共享的问题,原理是集中式管理,主要有三个核心方法,action,store,reducer,
// 工作流程是 view 调用 store 的 dispatch 接收 action 传入 store,reducer 进行 state 操作,view 通过 store 提供的 getState 获取最新的数据,
// flux 也是用来进行数据操作的,有四个组成部分 action,dispatch,view,store,工作流程是 view 发出一个 action,派发器接收 action,让 store 进行数据更新,更新完成以后 store 发出 change,view 接受 change 更新视图。Redux 和 Flux 很像。主要区别在于 Flux 有多个可以改变应用状态的 store,在 Flux 中 dispatcher 被用来传递数据到注册的回调事件,但是在 redux 中只能定义一个可更新状态的 store,redux 把 store 和 Dispatcher 合并,结构更加简单清晰
// 新增 state,对状态的管理更加明确,通过 redux,流程更加规范了,减少手动编码量,提高了编码效率,同时缺点时当数据更新时有时候组件不需要,但是也要重新绘制,有些影响效率。一般情况下,我们在构建多交互,多数据流的复杂项目应用时才会使用它们
react类组件和函数组件区别与使用场景
webpack
生产环境,webpack如何加快编译速度
webpack的几大概念?都是做什么的
- entry
- output
- loader
- plugin
loader和plugins的区别和基本原理
网络
tcp
第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开TCP连接的请求,断开过程需要经过“四次握手”(过程就不细写了,就是服务器和客户端交互,最终确定断开)
http、https、http2
什么是进程、什么是线程
设计原则与设计模式
- 工厂模式
故名思意,我们从字面上的意思就可以看到,可以想象一座工厂源源不断产出一样的产品,流水线作业。没错,工厂模式就是这样。
- 单例模式
单例模式就是保证一个类仅有一个实例,并提供一个访问它的全局访问点。其实这有一点像我们vuex当中的实现,也是一个全局的状态管理,并且提供一个接口访问。
- 代理模式
我们在事件代理的时候其实就是使用了代理模式,通过把监听事件全部交由父节点进行监听,这样你添加节点或者删除节点的时候就不用去改变监听的代码。
- 发布订阅模式
这种模式在生活中随处可见,比如你订阅了一个网课,开始前10分钟就会提醒你去听课。这里其实就是发布-订阅的模式,你订阅了它的开课信息,但是你不会接收到另一门的开课信息,因为你没有订阅。
- 适配器模式
- 策略模式
- 迭代器模式
JS垃圾回收与V8垃圾回收机制
算法
排序
- 快速排序
- 插入排序
- 冒泡排序
- 选择排序
- 归并排序
快速排序
快速排序的基本思想就是分治法的思想,寻找中间点,并对其左右的序列递归进行排序,直到左右都排序完成。
function quickSort (arr) {
if (arr.length == 0) {
return arr
}
var pirotIndex = Math.floor(arr.length/2)
var pirot = arr.splice(pirotIndex,1)[0]
var left = [], right = []
for (var i = 0; i < arr.length; i++) {
if (arr[i] > pirot) {
right.push(arr[i])
} else {
left.push(arr[i])
}
}
return quickSort(left).concat(pirot, quickSort(right))
}
console.log(quickSort([2,4,6,1,7,8,4,9,99,6]))
插入排序
将数组分为无序区和有序区两个区,然后不断将无序区的第一个元素按大小顺序插入到有序区中去,最终将所有无序区元素都移动到有序区完成排序
冒泡排序
比较相邻的元素。如果第一个比第二个大,就交换他们两个。对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
function bubbleSort(arr){
if(arr.length==0){
return arr
}
for(var i=0;i<arr.length;i++){
for(j=0;j<arr.length-1;j++){
if(arr[j]>arr[j+1]){
// 交换位置
[arr[j],arr[j+1]]=[arr[j+1],arr[j]] //ES6解构
}
}
}
return arr
}
console.log(bubbleSort([2,4,6,1,7,8,4,9,99,6]))