第一套

一面 -> 口述问题

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; // 实例的原型的构造函数 指向 构造函数其本身
img

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

划重点!!!

  1. 先执行微任务,后执行宏任务。所以setTimeout最后输出
  2. Promise 没有执行resolve,所以then不会执行,不会输出promise2
  3. 若是Promise 执行了resolve,那么会输出async1,再输出promise2

3、你常用的ES6语法有哪些

  1. 扩展运算符 ...
  2. const, let, () => {} 箭头函数
  3. 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的优化,你都做了哪些

  1. tree sharking 删除无用的代码
  2. html-webpack-plugin 给html文件加上js,不用手动引入,压缩js
  3. optimization 文件压缩,合并重复的代码块
  4. splitChunks 拆分代码块
  5. imagemin-webpack-plugin 压缩图片
  6. 拆分多入口编译

一面 -> 手写代码

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、写一个深拷贝函数

请看手写代码系列 -> clone

3、使用你擅长的框架,写一个todolist的功能组件

第一套面试题open in new window

二面 -> 口述问题

1、vue中 watch和computed区别

  1. watch 是监听依赖属性,接收最新值newValue和老值oldValue,当依赖属性发生变化时,执行回调。

  2. computed 是计算属性,vue内部有做缓存,只有当它的依赖属性发生变化,它才会重新计算并触发渲染。否则不会再次触发计算。

2、浏览器缓存,刷新和强刷(键盘按F5和按Ctrl + F5)浏览器处理缓存的流程

缓存分强缓存和协商缓存。

  • 刷新:不会更新本地缓存的文件。

  • 强刷:会清除本地已缓存的文件,重新从服务器拉取资源。

强缓存:

不会向服务器发送请求,直接从缓存中读取数据。从chrome控制台network中看到该请求返回200,并且size显示from disk cache或from memory cache。

  1. Expires 绝对时间,超出时间缓存失效。受限于本地时间,如果修改了本地时间,缓存可能会失效。

  2. Cache-control 最大缓存时间戳

协商缓存:

就是等强制缓存失效后,浏览器携带缓存标识决定是否使用缓存的过程。

  1. Last-Modified && lf-Modified-Since 最后修改时间,只要修改时间一变,就会重新请求资源。「不管最后文件到底有没有修改,只要最后修改时间对不上就不行」

  2. Etag && If-None-Match 是否匹配,跟指纹一样,只有当文件内容发生改变时,就会产生新的Etag,就会重新请求资源。

缓存优先级: Cache-control > Expire > Etag > Last-Modified

浏览器处理缓存的流程:

  1. 当用户请求资源时,先看本地是否有缓存。
  2. 无缓存的话,向服务器请求资源,资源响应回来后,看是否需要缓存,需要缓存就缓存在本地。
  3. 若有缓存,看是否过期,未过期,直接从缓存读出资源。
  4. 已过期,看报文用的是什么协商缓存,发送请求时带上对应的标识过去。由服务器判断。 Etag -> If-None-Match, Last-Modified -> If-Modified-Since
  5. 若未失效,服务器返回304。 已失效,服务器返回200,重新请求数据。
  6. 渲染页面

3、然后根据简历,聊你的项目,你简历项目列出的那些点,都如何实现的,解决思路是啥等等

4、git问题,你已经commit了5次,你现在想取消这5次,该为1次commit,你如何处理

rebase

git rebase -i HEAD~5

二面 -> 手写代码

1、使用你擅长的框架,写一个双栏穿梭选择框组件,功能类似如下图:

img

第一套面试题open in new window

Last Updated:
Contributors: kk