相关面经
01. Vue的Diff算法?详细谈谈。
相关文章:
回答要点
Diff算法是一种对比算法。对比两者是
旧虚拟DOM和新虚拟DOM,对比出是哪个虚拟节点更改了,找出这个虚拟节点,并只更新这个虚拟节点所对应的真实节点,而不用更新其他数据没发生改变的节点,实现精准地更新真实DOM,进而提高效率。
02. 手写Call/Apply/Bind函数
相关文章
Call
Function.prototype.myCall = function(context,...args){
let cxt = context || window;
//将当前被调用的方法定义在cxt.func上.(为了能以对象调用形式绑定this)
//新建一个唯一的Symbol变量避免重复
let func = Symbol()
cxt[func] = this;
args = args ? args : []
//以对象调用形式调用func,此时this指向cxt 也就是传入的需要绑定的this指向
const res = args.length > 0 ? cxt[func](...args) : cxt[func]();
//删除该方法,不然会对传入对象造成污染(添加该方法)
delete cxt[func];
return res;
}
Apply
Function.prototype.myApply = function(context,args = []){
let cxt = context || window;
//将当前被调用的方法定义在cxt.func上.(为了能以对象调用形式绑定this)
//新建一个唯一的Symbol变量避免重复
let func = Symbol()
cxt[func] = this;
//以对象调用形式调用func,此时this指向cxt 也就是传入的需要绑定的this指向
const res = args.length > 0 ? cxt[func](...args) : cxt[func]();
delete cxt[func];
return res;
}
// 1. 用 `apply` 将数组各项添加到另一个数组
var array = ['a', 'b'];
var elements = [0, 1, 2];
array.push.apply(array, elements);
console.info(array); // ["a", "b", 0, 1, 2]
// 2. 使用apply和内置函数
/* 找出数组中最大/小的数字 */
var numbers = [5, 6, 2, 3, 7];
var max = Math.max.apply(null, numbers);
var min = Math.min.apply(null, numbers);
/* 基本等同于 Math.max(numbers[0], ...) 或 Math.max(5, 6, ..) */
Bind
Function.prototype.myBind = function (context, ...args) {
//新建一个变量赋值为this,表示当前函数
const fn = this
//判断有没有传参进来,若为空则赋值[]
args = args ? args : []
//返回一个newFn函数,在里面调用fn
return function newFn(...newFnArgs) {
if (this instanceof newFn) {
return new fn(...args, ...newFnArgs)
}
return fn.apply(context, [...args,...newFnArgs])
}
}
03. 前端路由了解吗?有什么区别?
相关文章
回答要点:
1、为什么会出现前端路由。
2、前端路由解决了什么问题。
3、前端路由实现的原理是什么。
04. Babel的原理是什么
相关文章
面试官(7): 聊一聊 Babel?
webpack系列之:babel的原理
回答要点:
- 解析Parse: 将代码(其实就是字符串)转换成 AST( 抽象语法树)
- 转换Transform: 访问 AST 的节点进行变换操作生成新的 AST
- 生成Generate: 以新的 AST 为基础生成代码
词法分析和语法分析的作用?
- a=1;会进行词法分析吗?
- 怎么进行词法分析的?(关键字)
- 词法和语法谁先执行?
- 哪些算是词法哪些算是语法?
- const const a=1;词法分析能通过吗?是到语法分析才报错吗?
05. 实现一个发布订阅模式,手写EventEmmiter
相关文章
interface SubscribeEvent {
fn: Function;
once: boolean;
}
type CacheArgs = Array<any>;
class EventEmmiter {
subscribes: Map<string, Array<SubscribeEvent>>;
_cacheQueue: Map<string, Array<CacheArgs>>;
constructor() {
this.subscribes = new Map();
this._cacheQueue = new Map();
}
addEvent(type: string, callback: Function, once: boolean = false) {
const cache = this._cacheQueue.get(type) || [];
if(cache.length !== 0) {
cache.forEach(args => {
callback(...args);
})
this._cacheQueue.delete(type);
}
const sub = this.subscribes.get(type) || [];
sub.push({ fn: callback, once });
this.subscribes.set(type, sub);
}
on(type: string, callback: Function) {
this.addEvent(type, callback);
}
emit(type: string, ...args: Array<any>) {
const sub = this.subscribes.get(type) || [];
if(sub.length === 0) {
const cache = this._cacheQueue.get(type) || [];
cache.push(args)
this._cacheQueue.set(type, cache);
} else {
const context = this;
sub.forEach(({ fn }) => {
fn.call(context, ...args);
});
const newSub = sub.filter(item => !item.once);
this.subscribes.set(type, newSub);
}
}
off(type: string, callback: Function) {
const sub = this.subscribes.get(type);
if(sub) {
const newSub = sub.filter(({ fn }) => fn !== callback);
this.subscribes.set(type, newSub);
}
}
once(type: string, callback: Function) {
this.addEvent(type, callback, true);
}
}
const eventEmmiter = new EventEmmiter();
eventEmmiter.emit('test_cache', 1, 2);
eventEmmiter.emit('test_cache', 1, 3);
eventEmmiter.on('test_cache', (a: number, b: number) => {
console.log("事件发布后才订阅的, 计算的值为",a + b);
});
eventEmmiter.on('test_cache', (a: number, b: number) => {
console.log("已没有发布事件的缓存了, 不会触发",a + b);
});
06. 说一说Proxy
相关文章
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
const p = new Proxy(target, handler)
- `target`: 要使用 `Proxy` 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
- `handler`: 一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 `p` 的行为。
07. Webpack的工作原理
基本概念
- Entry 入口,webpack执行构建的第一步将从Entry开始,可抽象成输入
- Module 模块,在webpack里一切皆模块,一个模块对应着一个文件。webpack会从配置的Entry开始递归找出所有依赖的模块。
- Chunk 代码块,一个Chunk由多个模块组合而成,用于代码合并与分割。
- Loader 模块转换器,用于把模块原内容按照需求转换成新内容。
- Plugin 扩展插件,在webpack构建流程中特定时机会广播出对应的事件,插件可以监听这些事件的发生,在特定时机做对应的事情。
运行流程:
- 初始化参数:从配置文件和Shell语句中读取与合并参数,得出最终的参数;
- 开始编译: 用上一步得到的参数初始化Complier对象,加载所有配置的插件,执行对象的run方法开始执行编译;
- 确定入口: 根据配置中的entry找出所有入口文件;
- 编译模块:从入口文件出发,调用所有配置的Loader对模块进行翻译,再找出该模块依赖的模块,再递归本步骤知道所有入口依赖的文件都经过了本步骤的处理;
- 完成模块编译: 在经过第4步使用Loader翻译完所有模块后,得到了每个模块被翻译后的最终内容以及他们之间的依赖关系;
- 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的Chunk,再把每个Chunk转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
- 输出完成: 在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。
Loader
一个Loader其实就是一个Node.js模块,这个模块需要导出一个函数。这个导出函数的工作就是获得处理前的原内容,对原内容执行处理后,返回处理后的内容。
module.exports = function(source) {
// source为compiler传递给Loader的一个文件的原内容
// 该函数需要返回处理后的内容,这里简单起见,直接把原内容返回了,相当于该Loader没有做任何转换
return source
}
// 由于Loader运行在Node.js中,你可以调用任何Node.js自带的API,或者安装第三方模块进行调用:
const sass = require('node-sass')
module.exports = function(source) {
return sass(source)
}
08. 前端性能监控有了解吗?
相关文章
09. 怎么防止重复请求
let count = 1;
// 模拟请求
let promiseFunction = () =>
new Promise(rs =>
setTimeout(() => {
rs(count++);
})
);
// 如果发了多个请求,只执行
function firstPromise (promiseFn) {
let p = null
return function (...args) {
return p
? p
: (p = promiseFn.apply(this, args)).finally(() => (p = null))
}
}
let firstFn = firstPromise(promiseFunction);
firstFn().then(console.log); // 1
firstFn().then(console.log); // 1
firstFn().then(console.log); // 1
10. 求两数之和
/**
* 求一个数组种和等于target的2个元素的下标
* 把遍历过的值用一个对象存起来,
* 遍历过程中看看临时对象是否存在`当前值的差值`,有直接返回即结束。
*/
let nums = [2, 7, 11, 15]
let target = 9
function getIndexes(arr, tgt) {
let temp = {}
for (let i = 0; i < arr.length; i++) {
let value = arr[i]
let diff = target - value
if (temp[diff] === 0 || temp[diff]) {
return [temp[diff], i]
} else {
temp[value] = i
}
}
}
console.log('age', getIndexes(nums,target));
11. 白屏优化方案
当前很多无线页面都使用前端模板进行数据渲染,那么在糟糕的网速情况下,一进去页面,看到的不是白屏就是 loading,这成为白屏问题。
此问题发生的原因基本可以归结为网速跟静态资源,根本原因是客户端渲染的无力
- 1、css文件加载需要一些时间,在加载的过程中页面是空白的。 解决:可以考虑将css代码前置和内联。
- 2、首屏无实际的数据内容,等待异步加载数据再渲染页面导致白屏。 解决:在首屏直接同步渲染html,后续的滚屏等再采用异步请求数据和渲染html。
- 3、首屏内联js的执行会阻塞页面的渲染。 解决:尽量不在首屏html代码中放置内联脚本。(来自翔歌)
解决方案
- 因此最简单的方法是在服务器端,使用模板引擎渲染所有页面。同时
- 减少文件加载体积,如html压缩,js压缩
- 加快js执行速度 比如常见的无限滚动的页面,可以使用js先渲染一个屏幕范围内的东西
- 提供一些友好的交互,比如提供一些假的滚动条
- 使用本地存储处理静态文件。
12. 手写防抖,节流
- 连续触发在最后一次执行方法,场景:输入框匹配
// 防抖
let debounce = (fn, time = 1000) => {
let timeLock = null
return function (...args){
clearTimeout(timeLock)
timeLock = setTimeout(()=>{
fn(...args)
}, time)
}
}
13. 说说浏览器的消息循环机制
14. transition和animation的区别
15. 说说requestAnimationFrame的作用,并实现获取每秒的帧数
16. 浏览器的控制台是怎么渲染的?说说在浏览器控制台输出console到输出显示的过程?
17. DNS相关
- DNS解析是去哪找的缓存?
- 怎么找到DNS服务器?
- DNS怎么解析出IP的?
- 解析出ip地址后怎么找到对方?
- 握手为什么要三次?万一第三次没有发出去呢?
18. 算法-复原IP地址
19. CSS实现一个正三角形
20. 说说header常见的字段有哪些?作用分别是什么?
Access-Control-Request-Method
21. Cookie有哪些字段?分别什么作用
22. CSRF怎么防范?
23. 单页和多页应用怎么通讯
24. 说说HTTP和HTTPS的区别?
- 主要从HTTPS解决了什么问题出发分点描述
25. 一个扫码支付的功能你会怎么实现
26. 说说new的过程吧
27. 说说原型链
28. this相关的几道题目
29. 如何防范iframe被钓鱼网站嵌套导致的安全问题?iframe如何判断是否被嵌套?
30. 实现一个深拷贝你会考虑到哪些点?
31. 实现两个有序链表的合并
32. 聊聊你知道的设计模式有哪些?
33. 讲讲函数式编程的特点
34. 给你一段template,写出编译成render函数后的代码
35. 说说Vue的依赖收集过程吧,当数据发生变化后,依赖会重新收集吗
36. 讲讲webpack的原理吧,你掌握到哪种程度?
优化插件、打包原理和热更新原理,看过核心源码
loader和plugin实现过吗?知道原理吗?
说说module、chunk、bundle、asset的区别
- chunk一定是通过入口生成的吗?(不一定,import动态加载的模块也会作为一个chunk)
- css-loader的作用?(处理依赖关系)
- css中的路径是如何解析的?
- css-loader和file-loader如何一起工作的?