第一套
一面 -> 口述问题
1、原型链
在Javascript中,除了null以外都具备的属性__proto__, 会指向该构造函数的原型prototype。
在读取对象的某个属性时,Javascript引擎会先寻找对象本身的属性,如果找不到,就会通过 __proto__ 查找原型中的属性。如果原型还是找不到,就到原型的原型去找。直到找到最顶层Object.prototype,还是找不到的, 则返回null。
通过 __proto__ 像链条一样连接起来的就称为原型链。
const num = new Number(1);
num.__proto__ === Number.prototype; // 实例的原型 指向 构造函数的原型
num.__proto__.__proto__ === Object.prototype; // 实例的原型的原型 指向 Object的原型
num.__proto__.constructor === Number; // 实例的原型的构造函数 指向 构造函数其本身

2、js运行机制
众所周知,javascript是一门单线程的语言。
javascript的单线程决定只有当一个任务结束后才能执行下一个任务,否则下一个任务只能处在等待状态。而任务分同步任务和异步任务。
EventLoop事件循环,指的是浏览器与Node异步队列调度的过程。先执行微任务,如Promise, Ajax, mutationObserver等。再执行宏任务,如setTimeout, setInterval等。当执行宏任务时,碰到了微任务,先把微任务执行完,再执行下一个宏任务。
这一点,浏览器与Node不同,Node版本小与11.0时,在执行宏任务时,若碰到了微任务,不管微任务,先把宏任务全部执行完后再执行微任务。从11.0开始,与浏览器统一标准。
联想:面试经典题
console.log('start')
async function async1() {
console.log("async1");
await async2();
console.log("async1");
}
async function async2() {
console.log( 'async2');
}
console.log("script");
setTimeout(function () {
console.log("setTimeout");
},0);
async1();
new Promise(function () {
console.log("promise1");
}).then(function () {
console.log("promise2");
});
// output:
start
script
async1
async2
promise1
async1
setTimeout
划重点!!!
- 先执行微任务,后执行宏任务。所以setTimeout最后输出
- Promise 没有执行resolve,所以then不会执行,不会输出promise2
- 若是Promise 执行了resolve,那么会输出async1,再输出promise2
3、你常用的ES6语法有哪些
- 扩展运算符
... - const, let, () => {} 箭头函数
my name is ${name}模版字符串
4、babel的了解 (问你什么代码可以转ES5、哪些不可以转)
就是将浏览器无法解析的ES6+的语法,将代码转成抽象语法数,再由抽象语法数生成ES5的语法,让浏览器可以解析。
babel 默认只会转换新的js语法,不会转换新的api,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象。
如ES6新增的Array.from(), babel就不会解析。要用垫片,使用babel-polyfill。
5、webpack的优化,你都做了哪些
- tree sharking 删除无用的代码
- html-webpack-plugin 给html文件加上js,不用手动引入,压缩js
- optimization 文件压缩,合并重复的代码块
- splitChunks 拆分代码块
- imagemin-webpack-plugin 压缩图片
- 拆分多入口编译
一面 -> 手写代码
1、如何统计当前页面setTimeout定时器的执行次数(提示用call、apply)
let count = 0;
const originFunc = setTimeout;
var setTimeout = function () {
count++;
return originFunc(...arguments);
}
setTimeout(() => {
console.log('test');
}, 500);
setTimeout(() => {
console.log('test');
}, 500);
setTimeout(() => {
console.log('test');
}, 500);
2、写一个深拷贝函数
3、使用你擅长的框架,写一个todolist的功能组件
二面 -> 口述问题
1、vue中 watch和computed区别
watch 是监听依赖属性,接收最新值newValue和老值oldValue,当依赖属性发生变化时,执行回调。
computed 是计算属性,vue内部有做缓存,只有当它的依赖属性发生变化,它才会重新计算并触发渲染。否则不会再次触发计算。
2、浏览器缓存,刷新和强刷(键盘按F5和按Ctrl + F5)浏览器处理缓存的流程
缓存分强缓存和协商缓存。
刷新:不会更新本地缓存的文件。
强刷:会清除本地已缓存的文件,重新从服务器拉取资源。
强缓存:
不会向服务器发送请求,直接从缓存中读取数据。从chrome控制台network中看到该请求返回200,并且size显示from disk cache或from memory cache。
Expires 绝对时间,超出时间缓存失效。受限于本地时间,如果修改了本地时间,缓存可能会失效。
Cache-control 最大缓存时间戳
协商缓存:
就是等强制缓存失效后,浏览器携带缓存标识决定是否使用缓存的过程。
Last-Modified && lf-Modified-Since 最后修改时间,只要修改时间一变,就会重新请求资源。「不管最后文件到底有没有修改,只要最后修改时间对不上就不行」
Etag && If-None-Match 是否匹配,跟指纹一样,只有当文件内容发生改变时,就会产生新的Etag,就会重新请求资源。
缓存优先级: Cache-control > Expire > Etag > Last-Modified
浏览器处理缓存的流程:
- 当用户请求资源时,先看本地是否有缓存。
- 无缓存的话,向服务器请求资源,资源响应回来后,看是否需要缓存,需要缓存就缓存在本地。
- 若有缓存,看是否过期,未过期,直接从缓存读出资源。
- 已过期,看报文用的是什么协商缓存,发送请求时带上对应的标识过去。由服务器判断。 Etag -> If-None-Match, Last-Modified -> If-Modified-Since
- 若未失效,服务器返回304。 已失效,服务器返回200,重新请求数据。
- 渲染页面
3、然后根据简历,聊你的项目,你简历项目列出的那些点,都如何实现的,解决思路是啥等等
略
4、git问题,你已经commit了5次,你现在想取消这5次,该为1次commit,你如何处理
rebase
git rebase -i HEAD~5
二面 -> 手写代码
1、使用你擅长的框架,写一个双栏穿梭选择框组件,功能类似如下图:
