近期面试128题汇总(含超详细答案)

前言

你盼世界,我盼望你无 bug
。Hello 大家好!我是霖呆呆!

哈哈:smile:,这是一篇面试总结文章,抱歉, webpack
HTTP
系列的先暂缓一下更新哈,Sorry~
这篇文章是呆呆自己近期的一些面试汇总,算了一下有128道,基本都写了比较完善的答案,就算没有写也有推荐一些好的文章链接,文章篇幅较大,整理总结也花费了很多的时间和心血,题目也是根据公司规模的大小从易到难,算是见证了呆呆面试过程中的不足与成长吧 :sweat_smile:。还希望能帮助到正在一起努力求生存的小伙伴们。

"风浪没平息 我宣告奔跑的意义"

"这不是叛逆 我只是淋了一场雨"

所有文章内容都已整理至 https://github.com/LinDaiDai/niubility-coding-js 快来给我Star呀:blush:~
(因为近期面的主要都是一些深圳的中小公司,他们也还在招聘中,所以不便透露公司名称,还请大家理解…)

深圳某做海外加速器公司

4月22日上午

1. DIV+CSS布局的好处

  1. 代码精简,且结构与样式分离,易于维护
  2. 代码量减少了,减少了大量的带宽,页面加载的也更快,提升了用户的体验
  3. 对SEO搜索引擎更加友好,且H5又新增了许多语义化标签更是如此
  4. 允许更多炫酷的页面效果,丰富了页面
  5. 符合W3C标准,保证网站不会因为网络应用的升级而被淘汰

缺点:
不同浏览器对web标准默认值不同,所以更容易出现对浏览器的兼容性问题。

2. 如何解决a标点击后hover事件失效的问题?

改变a标签css属性的排列顺序

只需要记住 LoVe HAte
原则就可以了:

link→visited→hover→active

比如下面错误的代码顺序:

a:hover{
color: green;
text-decoration: none;
}
a:visited{ /* visited在hover后面,这样的话hover事件就失效了 */
color: red;
text-decoration: none;
}

正确的做法是将两个事件的位置调整一下。
注意:warning:各个阶段的含义:

a:link
:未访问时的样式,一般省略成a a:visited
:已经访问后的样式 a:hover
:鼠标移上去时的样式 a:active
:鼠标按下时的样式

3. 点击一个input依次触发的事件

const text = document.getElementById('text');
text.onclick = function (e) {
console.log('onclick')
}
text.onfocus = function (e) {
console.log('onfocus')
}
text.onmousedown = function (e) {
console.log('onmousedown')
}
text.onmouseenter = function (e) {
console.log('onmouseenter')
}

答案:

'onmouseenter'
'onmousedown'
'onfocus'
'onclick'

4. 响应式的好处

对某些数据的修改就能自动更新视图,让开发者不用再去操作DOM,有更多的时间去思考业务逻辑。

5. Vue的优点及缺点

首先Vue最核心的两个特点, 「响应式」
「组件化」

「响应式」:这也就是vue.js最大的优点,通过MVVM思想实现数据的双向绑定,通过虚拟DOM让我们可以用数据来操作DOM,而不必去操作真实的DOM,提升了性能。且让开发者有更多的时间去思考业务逻辑。

「组件化」:把一个单页应用中的各个模块拆分到一个个组件当中,或者把一些公共的部分抽离出来做成一个可复用的组件。所以组件化带来的好处就是,提高了开发效率,方便重复使用,使项目的可维护性更强。

「虚拟DOM」,当然,这个不是vue中独有的。

「缺点」:基于对象配置文件的写法,也就是options写法,开发时不利于对一个属性的查找。另外一些缺点,在小项目中感觉不太出什么,vuex的魔法字符串,对ts的支持。兼容性上存在一些问题。

6. Vue中hash模式和history模式的区别

  • hash
    URL
    #
    history
    
  • Vue
    hash
    onhashchange
    location.hash
    history
    HTML5 history
    pushState()
    url
    replaceState()
    
  • URL
    HTTP
    URL
    history
    history
    URL
    URL
    URL
    path
    www.lindaidai.wang/blogs/id
    404
    404
    

hash:

window.onhashchange = function(event){
// location.hash获取到的是包括#号的,如"#heading-3"
// 所以可以截取一下
let hash = location.hash.slice(1);
}

7. react的实现原理?有什么优缺点?

(技术栈是 Vue
,没用过 react
)

8. react的控制组件和非控制组件

同上

深圳某电商公司

4月22日下午

1. null和undefined的区别

  • null
    表示一个 "无"
    的对象,也就是该处不应该有值;而 undefined
    表示 「未定义」
  • Number(null)
    0
    undefined
    NaN
    

使用场景上:

null

  • 作为函数的参数,表示该函数的参数不是对象
  • 作为对象原型链的终点

undefined
:

  • 变量被声明了,但没有赋值时,就等于undefined
  • 调用函数时,应该提供的参数没有提供,该参数等于undefined
  • 对象没有赋值属性,该属性的值为undefined
  • 函数没有返回值时,默认返回undefined

2. 冒泡排序算法和数组去重

「冒泡排序」:

function bubbleSort (arr) {
for (let i = 0; i < arr.length; i++) {
let flag = true;
for (let j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
flag = false;
let temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
if (flag) break;
}
}

这个是优化过后的冒泡排序。用了一个 flag
来优化,它的意思是:如果 「某一次」
循环中没有交换过元素,那么意味着排序已经完成了。
冒泡排序总会执行(N-1)+(N-2)+(N-3)+..+2+1趟,但如果运行到当中某一趟时排序已经完成,或者输入的是一个有序数组,那么后边的比较就都是多余的,为了避免这种情况,我们增加一个flag,判断排序是否在中途就已经完成(也就是判断有无发生元素交换)

「数组去重」:

  1. Array.form(new Set(arr))
  2. [...new Set(arr)]
  3. for
    循环嵌套,利用 splice
    去重
  4. 新建数组,利用 indexOf
    或者 includes
    去重
  5. sort
    0
    while
    

当然还有很多,例如用 filter、reduce、Map、Object
等,具体可以看:
JavaScript数组去重(12种方法):https://segmentfault.com/a/1190000016418021

Array.form(new Set(arr))
[...new Set(arr)]

var arr = [1,1,2,5,6,3,5,5,6,8,9,8];
console.log(Array.from(new Set(arr)))
// console.log([...new Set(arr)])

「for循环嵌套,利用splice去重」:

function unique (origin) {
let arr = [].concat(origin);
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] == arr[j]) {
arr.splice(j, 1);
j--;
}
}
}
return arr;
}
var arr = [1,1,2,5,6,3,5,5,6,8,9,8];
console.log(unique(arr))

「新建数组,利用indexOf去重」:

function unique (arr) {
let res = []
for (let i = 0; i < arr.length; i++) {
if (!res.includes(arr[i])) {
res.push(arr[i])
}
}
return res;
}
var arr = [1,1,2,5,6,3,5,5,6,8,9,8];
console.log(unique(arr))

「先用sort排序,然后用一个指针从第0位开始,配合while循环去重」:

function unique (arr) {
arr = arr.sort(); // 排序之后的数组
let pointer = 0;
while (arr[pointer]) {
if (arr[pointer] != arr[pointer + 1]) { // 若这一项和下一项不相等则指针往下移
pointer++;
} else { // 否则删除下一项
arr.splice(pointer + 1, 1);
}
}
return arr;
}
var arr = [1,1,2,5,6,3,5,5,6,8,9,8];
console.log(unique(arr))

深圳某云产品公司

4月23日上午
(从这家公司开始面试稍微有些难度了,面试官小哥哥人也很好,刚开始是一个高冷男神,但是在呆呆的猛烈回答下终于还是对我露出了微笑:smile:,还说他也是掘友,有看过我的文章…掘友真是无处不在啊,感动:joy_cat:)

1. 描述一下Promise

这道题我会先大概介绍一下 Promise

Promise
是一个对象,它代表了一个异步操作的最终完成或者失败。由于它的 then
方法和 catch、finally
方法会返回一个新的 Promise
所以可以允许我们链式调用,解决了传统的回调地狱问题。

再说一下 then
以及 catch
方法:

(此处我是直接拿我之前的一篇文章《45道Promise题》那里的总结)

  1. Promise
    的状态一经改变就不能再改变。(见3.1)
  2. .then
    .catch
    Promise
    
  3. catch
    不管被连接到哪里,都能捕获上层未捕捉过的错误。(见3.2)
  4. Promise
    promise
    promise
    return 2
    return Promise.resolve(2)
    
  5. Promise
    .then
    .catch
    Promise
    .then
    .catch
    
  6. .then
    .catch
    return
    error
    .catch
    
  7. .then
    .catch
    返回的值不能是 promise 本身,否则会造成死循环。(见3.7)
  8. .then
    或者 .catch
    的参数期望是函数,传入非函数则会发生值透传。(见3.8)
  9. .then
    catch
    .then
    
  10. .finally
    Promise
    Promise
    resolved
    rejected
    

另外也可以说一下 finally
方法:

  1. .finally()
    方法不管 Promise
    对象最后的状态如何都会执行

  2. .finally()
    方法的回调函数不接受任何的参数,也就是说你在 .finally()
    函数中是没法知道 Promise
    最终的状态是 resolved
    还是 rejected

  3. 它最终返回的默认会是一个 「上一次的Promise对象值」
    ,不过如果抛出的是一个异常则返回异常的 Promise
    对象。

最后可以说一下 all
以及 race
方法:

  • Promise.all()
    的作用是接收一组异步任务,然后并行执行异步任务,并且在所有异步操作执行完后才执行回调。
  • .race()
    的作用也是接收一组异步任务,然后并行执行异步任务,只保留取第一个执行完成的异步操作的结果,其他的方法仍在执行,不过执行结果会被抛弃。
  • Promise.all().then()
    结果中数组的顺序和 Promise.all()
    接收到的数组顺序一致。
  • all和race
    then
    catch
    

2. Promise.all中如果有一个抛出异常了会如何处理

这个,在上一题已经说到了:

all和race
传入的数组中如果有会抛出异常的异步任务,那么只有最先抛出的错误会被捕获,并且是被 then
的第二个参数或者后面的 catch
捕获;但并不会影响数组中其它的异步任务的执行。

3. Promise为什么能链式调用

由于它的 then
方法和 catch、finally
方法会返回一个新的 Promise
所以可以允许我们链式调用

4. 描述一下EventLoop的执行过程

  • 一开始整个脚本作为一个宏任务执行
  • 执行过程中同步代码直接执行,宏任务进入宏任务队列,微任务进入微任务队列
  • 当前宏任务执行完出队,检查微任务列表,有则依次执行,直到全部执行完
  • 执行浏览器UI线程的渲染工作
  • 检查是否有 Web Worker
    任务,有则执行

  • 执行完本轮的宏任务,回到2,依此循环,直到宏任务和微任务队列都为空

(具体可以看这里:https://juejin.im/post/5e58c618e51d4526ed66b5cf#heading-1)

5. docoment,window,html,body的层级关系

「层级关系」:

window > document > html > body
  • window
    BOM
    的核心对象,它一方面用来获取或设置浏览器的属性和行为,另一方面作为一个全局对象。
  • document
    对象是一个跟文档相关的对象,拥有一些操作文档内容的功能。但是地位没有 window
    高。
  • html
    document
    html
    DOM
    html
    div、select
    

(我是这样记的,整个浏览器中最大的肯定就是窗口 window
了,那么进来的我不管你是啥,就算你是 document
也得给我盘着)

6. addEventListener函数的第三个参数

第三个参数涉及到冒泡和捕获,是 true
时为捕获,是 false
则为冒泡

7. 有写过原生的自定义事件吗

  • 使用 Event

  • 使用 customEvent
    (可以传参数)

  • 使用 document.createEvent('CustomEvent')和initCustomEvent()

「创建自定义事件」

原生自定义事件有三种写法:

  1. 使用 Event
let myEvent = new Event('event_name');
  1. 使用 customEvent
    (可以传参数)
let myEvent = new CustomEvent('event_name', {
detail: {
// 将需要传递的参数放到这里
// 可以在监听的回调函数中获取到:event.detail
}
})
  1. 使用 document.createEvent('CustomEvent')和initCustomEvent()
let myEvent = document.createEvent('CustomEvent');// 注意这里是为'CustomEvent'
myEvent.initEvent(
// 1. event_name: 事件名称
// 2. canBubble: 是否冒泡
// 3. cancelable: 是否可以取消默认行为
)
  • createEvent
    :创建一个事件
  • initEvent
    :初始化一个事件

可以看到, initEvent
可以指定3个参数。

(有些文章中会说还有第四个参数 detail
,但是我查看了 W3C
上并没有这个参数,而且实践了一下也没有效果)

「事件的监听」

自定义事件的监听其实和普通事件的一样,使用 addEventListener
来监听:

button.addEventListener('event_name', function (e) {})

「事件的触发」

触发自定义事件使用 dispatchEvent(myEvent)

注意:warning:,这里的参数是要自定义事件的对象(也就是 myEvent
),而不是自定义事件的名称( 'myEvent'
)

「案例」

来看个案例吧:

// 1.
// let myEvent = new Event('myEvent');
// 2.
// let myEvent = new CustomEvent('myEvent', {
// detail: {
// name: 'lindaidai'
// }
// })
// 3.
let myEvent = document.createEvent('CustomEvent');
myEvent.initEvent('myEvent', true, true)

let btn = document.getElementsByTagName('button')[0]
btn.addEventListener('myEvent', function (e) {
console.log(e)
console.log(e.detail)
})
setTimeout(() => {
btn.dispatchEvent(myEvent)
}, 2000)

8. 冒泡和捕获的具体过程

冒泡指的是:当给某个目标元素绑定了事件之后,这个事件会依次在它的父级元素中被触发(当然前提是这个父级元素也有这个同名称的事件,比如子元素和父元素都绑定了 click
事件就触发父元素的 click
)。
捕获则是从上层向下层传递,与冒泡相反。
(非常好记,你就想想水底有一个泡泡从下面往上传的,所以是冒泡)
来看看这个例子:


<ul onclick="alert('ul')">
<li onclick="alert('li')">
<button onclick="alert('button')">点击</button>
</li>
</ul>
<script>
window.addEventListener('click', function (e) {
alert('window')
})
document.addEventListener('click', function (e) {
alert('document')
})
</script>

冒泡结果: button > li > ul > document > window

捕获结果: window > document > ul > li > button

9. 所有的事件都有冒泡吗?

并不是所有的事件都有冒泡的,例如以下事件就没有:

  • onblur
  • onfocus
  • onmouseenter
  • onmouseleave

11. 描述下原型链

12. 手写new

function myNew (fn, ...args) {
let instance = Object.create(fn.prototype);
let result = fn.call(instance, ...args)
return typeof result === 'object' ? result : instance;
}

13. typeof和instanceof的区别

typeof
表示是对某个变量类型的检测,基本数据类型除了 null
都能正常的显示为对应的类型,引用类型除了函数会显示为 'function'
,其它都显示为 object

instanceof
它主要是 「用于检测某个构造函数的原型对象在不在某个对象的原型链上」

14. typeof为什么对null错误的显示

这只是 JS 存在的一个悠久 Bug。在 JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象然而 null 表示为全零,所以将它错误的判断为 object 。

15. 详细说下instanceof

instanceof
它主要是 「用于检测某个构造函数的原型对象在不在某个对象的原型链上」

算了,直接手写实现吧:

function myInstanceof (left, right) {
let proto = Object.getPrototypeOf(left);
while (true) {
if (proto === null) return false;
if (proto === right.prototype) return true;
proto = Object.getPrototypeOf(proto)
}
}

16. 一句话描述一下this

指向最后调用函数的那个对象,是函数运行时内部自动生成的一个内部对象,只能在函数内部使用

17. 函数内的this是在什么时候确定的?

函数调用时,指向最后调用的那个对象

18. apply/call/bind的相同和不同

19. webpack中的loader和plugin有什么区别

(答案参考童欧巴的一篇 webpack
面试文章哦: 「吐血整理」再来一打Webpack面试题(持续更新)
)

loader它是一个转换器,只专注于转换文件这一个领域,完成压缩、打包、语言编译, 「它仅仅是为了打包」
。并且运行在打包之前。

而plugin是一个扩展器,它丰富了webpack本身,为其进行一些其它功能的扩展。 「它不局限于打包,资源的加载,还有其它的功能」
。所以它是在整个编译周期都起作用。

20. HTTP和TCP的不同

HTTP的责任是去定义数据,在两台计算机相互传递信息时,HTTP规定了每段数据以什么形式表达才是能够被另外一台计算机理解。
而TCP所要规定的是数据应该怎么传输才能稳定且高效的传递与计算机之间。
(还可以再扩展)

21. TCP和UDP的区别

  1. TCP是一个面向连接的、可靠的、基于字节流的传输层协议。
  2. UDP是一个面向无连接的传输层协议。

TCP为什么可靠,是因为它有三次握手来保证双方都有接受和发送数据的能力。
字节流服务:将大块数据分割为以报文段为单位的数据包进行管理

22. 介绍一下虚拟DOM

虚拟 DOM
本质就是用一个原生的 JavaScript
对象去描述一个 DOM
节点。是对真实 DOM
的一层抽象。

由于在浏览器中操作 DOM
是很昂贵的。频繁的操作 DOM
,会产生一定的性能问题,因此我们需要这一层抽象,在 patch
过程中尽可能地一次性将差异更新到 DOM
中,这样保证了 DOM
不会出现性能很差的情况。

另外还有很重要的一点,也是它的设计初衷,为了更好的跨平台,比如 Node.js
就没有 DOM
,如果想实现 SSR
(服务端渲染),那么一个方式就是借助 Virtual DOM
,因为 Virtual DOM
本身是 JavaScript
对象。

Vue2.x
中的虚拟 DOM
主要是借鉴了 snabbdom.js
Vue3
中借鉴 inferno.js
算法进行优化。

23. 盒模型

24. 输入URL到页面的呈现

看三元的《(1.6w字)浏览器灵魂之问,请问你能接得住几个?》
https://juejin.im/post/5df5bcea6fb9a016091def69
分别从网络,解析,渲染来说
面试的问题基本都答出来了,当然后面还有一个技术总监的电话面,主要是问了一些工作相关的问题。
其实这家公司开出的条件也挺让呆呆心动的,包括氛围感觉也挺好,只不过可能还不是自己想要的吧,所以最终也是没去,挺可惜的…如果面试我的那位小哥哥哥看到了这里,还请不要难过哈,我们江湖会再见的:joy:。

深圳某房地产公司

4月27日
一面

5道笔试题

并详细说一下前面三道
(额,呆呆能力有限只答出来了前面三道)

二面

1. JSON的原理以及手写一个实现

基本原理:主要就是利用 script
标签的 src
属性没有跨域的限制,通过指向一个需要访问的地址,由服务端返回一个预先定义好的 Javascript
函数的调用,并且将服务器数据以该函数参数的形式传递过来,此方法需要前后端配合完成。
执行过程:

  • 前端定义一个解析函数(如: jsonpCallback = function (res) {}
    )
  • params
    script
    cb=jsonpCallback
    
  • 后端获取到前端声明的执行函数( jsonpCallback
    ),并以带上参数且调用执行函数的方式传递给前端
  • 前端在 script
    标签返回资源的时候就会去执行 jsonpCallback
    并通过回调函数的方式拿到数据了。

缺点:

  • 只能进行 GET
    请求

优点:

  • 兼容性好,在一些古老的浏览器中都可以运行

代码实现:
(具体可以看我的这篇文章:JSONP原理及实现:https://www.jianshu.com/p/88bb82718517)

<script>
function JSONP({
url,
params = {},
callbackKey = 'cb',
callback
}
)
{
// 定义本地的唯一callbackId,若是没有的话则初始化为1
JSONP.callbackId = JSONP.callbackId || 1;
let callbackId = JSONP.callbackId;
// 把要执行的回调加入到JSON对象中,避免污染window
JSONP.callbacks = JSONP.callbacks || [];
JSONP.callbacks[callbackId] = callback;
// 把这个名称加入到参数中: 'cb=JSONP.callbacks[1]'
params[callbackKey] = `JSONP.callbacks[${callbackId}]`;
// 得到'id=1&cb=JSONP.callbacks[1]'
const paramString = Object.keys(params).map(key => {
return `${key}=${encodeURIComponent(params[key])}`
}).join('&')
// 创建 script 标签
const script = document.createElement('script');
script.setAttribute('src', `${url}?${paramString}`);
document.body.appendChild(script);
// id自增,保证唯一
JSONP.callbackId++;

}
JSONP({
url: 'http://localhost:8080/api/jsonps',
params: {
a: '2&b=3',
b: '4'
},
callbackKey: 'cb',
callback (res) {
console.log(res)
}
})
JSONP({
url: 'http://localhost:8080/api/jsonp',
params: {
id: 1
},
callbackKey: 'cb',
callback (res) {
console.log(res)
}
})
</script>

2. 浏览器为什么要跨域?如果是因为安全的话那小程序或者其他的为什么没有跨域?

跨域的产生来源于现代浏览器所通用的 同源策略
,所谓同源策略,是指只有在地址的:

  1. 协议名
  2. 域名
  3. 端口名

均一样的情况下,才允许访问相同的cookie、localStorage,以及访问页面的 DOM
或是发送 Ajax
请求。若在不同源的情况下访问,就称为跨域。
例如以下为同源:

http://www.example.com:8080/index.html
http://www.example.com:8080/home.html

以下为跨域:

http://www.example.com:8080/index.html
http://www3.example.com:8080/index.html

注意:warning::

但是有两种情况: http
默认的端口号为 80
https
默认端口号为 443

所以:

http://www.example.com:80 === http://www.example.com
https://www.example.com:443 === https://www.example.com

「为什么浏览器会禁止跨域?」

「简答」:

首先,跨域只存在于浏览器端,因为我们知道浏览器的形态是很开放的,所以我们需要对它有所限制。

其次,同源策略主要是为了保证用户信息的安全,可分为两种: Ajax
同源策略和 DOM
同源策略。

Ajax
同源策略主要是使得不同源的页面不能获取 cookie
且不能发起 Ajax
请求,这样在一定程度上防止了 CSRF
攻击。

DOM
同源策略也一样,它限制了不同源页面不能获取 DOM
,这样可以防止一些恶意网站在自己的网站中利用 iframe
嵌入正规的网站并迷惑用户,以此来达到窃取用户信息。

「深答」:

  • 首先,跨域只存在于浏览器端。浏览器它为 web
    提供了访问入口,并且访问的方式很简单,在地址栏输入要访问的地址或者点击某个链接就可以了,正是这种 「开放的形态」
    ,所以我们需要对它有所限制。

  • 所以同源策略它的产生主要是为了保证用户信息的安全,防止恶意的网站窃取数据。分为两种: Ajax
    同源策略与 DOM
    同源策略:

    同源策略它算是浏览器安全的第一层屏障吧,因为就像 CSRF
    攻击,它只能限制不同源页面 cookie
    的获取,但是攻击者还可能通过其它的方式来达到攻击效果。

    (注,上面提到的 iframe
    限制 DOM
    查询,案例如下)

    // HTML
    <iframe name="yinhang" src="www.yinhang.com"></iframe>
    // JS
    // 由于没有同源策略的限制,钓鱼网站可以直接拿到别的网站的Dom
    const iframe = window.frames['yinhang']
    const node = iframe.document.getElementById('你输入账号密码的Input')
    console.log(`拿到了这个${node},我还拿不到你刚刚输入的账号密码吗`)

    • Ajax
      cookie
      Ajax
      CSRF
      cookie
      Ajax
      cookie
      
    • DOM
      DOM
      iframe
      DOM
      

参考:

  • https://segmentfault.com/a/1190000015597029
  • https://juejin.im/post/5cad99796fb9a068ab40a29a

3. CORS跨域的原理

跨域资源共享( CORS
)是一种机制,是W3C标准。它允许浏览器向跨源服务器,发出 XMLHttpRequest
Fetch
请求。并且整个 CORS
通信过程都是浏览器自动完成的,不需要用户参与。

而使用这种 跨域资源共享
的前提是,浏览器必须支持这个功能,并且服务器端也必须同意这种 "跨域"
请求。因此实现 CORS
的关键是服务器需要服务器。通常是有以下几个配置:

  • 「Access-Control-Allow-Origin」

  • 「Access-Control-Allow-Methods」

  • 「Access-Control-Allow-Headers」

  • 「Access-Control-Allow-Credentials」

  • 「Access-Control-Max-Age」

具体可看:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS#Preflighted_requests
过程分析:

「简单回答」:

  • 当我们发起跨域请求时, 「如果是非简单请求」
    ,浏览器会帮我们自动触发预检请求,也就是 OPTIONS 请求,用于确认目标资源是否支持跨域。 「如果是简单请求,则不会触发预检,直接发出正常请求。」

  • 浏览器会根据服务端响应的 header 自动处理剩余的请求,如果响应支持跨域,则继续发出正常请求,如果不支持,则在控制台显示错误。

「详细回答」:

  • 浏览器先根据同源策略对前端页面和后台交互地址做匹配,若同源,则直接发送数据请求;若不同源,则发送跨域请求。
  • 服务器收到浏览器跨域请求后,根据自身配置返回对应文件头。若未配置过任何允许跨域,则文件头里不包含 Access-Control-Allow-origin
    字段,若配置过域名,则返回 Access-Control-Allow-origin + 对应配置规则里的域名的方式

  • 浏览器根据接受到的 响应头里的 Access-Control-Allow-origin
    字段做匹配,若无该字段,说明不允许跨域,从而抛出一个错误;若有该字段,则对字段内容和当前域名做比对,如果同源,则说明可以跨域,浏览器接受该响应;若不同源,则说明该域名不可跨域,浏览器不接受该响应,并抛出一个错误。

CORS
中有 简单请求
非简单请求
,简单请求是不会触发 CORS
的预检请求的,而非简单请求会。

“需预检的请求”
要求必须首先使用 OPTIONS
方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。”预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。
(关于更多CORS的内容可以看我的另一篇文章:CORS原理及实现:https://www.jianshu.com/p/b2bdf55e1bf5)

4. CORS预请求OPTIONS就一定是安全的吗?

5. 在深圳的网页上输入百度,是怎么把这个请求发到北京的

这个当时面试官和我说的是,中间会经过很多的站点,比如会经过湖南,或者其它城市,由各个城市的这些站点一层一层分发下去。

6. 输入URL到页面的呈现

7. Vue的响应式原理

8. 那在这个响应式中一个数据改变它是怎么通知要更新的,也就是如何把数据和页面关联起来?

面的最惨的一次…因为这次面试是当天下午6点才去面的,在这之前呆呆已经经过了3轮面试的折磨,所以身心疲惫很不在状态。当然最主要的是自己确实准备的还不够充分,其实现在回过头来看看这些题都不太难的…
当天也小小的自闭了一下,整理好状态第二天好好总结吧 :smile:。

深圳某海外直播公司

4月28日
(当时是电话面,一个小时20分钟,问了我大概五六十道题,我能想到的一共是50题,还有一些记不起来了)

1. CommonJS和ES6模块的区别

  • CommonJS模块是运行时加载,ES6 Modules是编译时输出接口
  • CommonJS输出是值的拷贝;ES6 Modules输出的是值的引用,被输出模块的内部的改变会影响引用的改变
  • CommonJs导入的模块路径可以是一个表达式,因为它使用的是 require()
    方法;而ES6 Modules只能是字符串
  • this
    this
    undefined
    
  • arguments
    require
    module
    exports
    __filename
    __dirname
    

关于第一个差异,是因为CommonJS 加载的是一个对象(即 module.exports
属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
(具体可以看我的这篇文章:https://juejin.im/post/5eaacd175188256d4345ea3a)

2. 模块的异步加载

模块的异步加载可以使用 AMD
或者 CMD
规范。
(具体可以看我的这篇文章:https://juejin.im/post/5eaacd175188256d4345ea3a)

3. 开发一个模块要考虑哪些问题?

封闭开放式原则、安全性
(应该还有,但是没想到)

4. 实现一个一组异步请求按顺序执行你有哪些方法?

  1. reduce
    Promise.resolve()
    .then()
    
  2. 利用 forEach
    ,本质和 reduce
    原理相同。(类似于这里https://juejin.im/post/5e58c618e51d4526ed66b5cf#heading-53)
  3. 还可以用 ES9
    中的 for...await...of
    来实现。

5. Promise.all()是并发的还是串行的?

并发的。不过 Promise.all().then()
结果中数组的顺序和 Promise.all()
接收到的数组顺序一致。

6. 平时写过哪些正则表达式

  • var trimReg = /(^\s+)|(\s+$)/g
    Vue
    .trim
    v-model.trim="msg"
    ES10
    trimStart
    trimEnd
    
  • 用于校验手机号的正则: var phoneReg = /^1[3456789]\d{9}$/g
  • 用正则写一个根据name获取cookie中的值的方法:
function getCookie(name) {
var match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]*)'));
if (match) return unescape(match[2]);
}

详细介绍可以看这里:每日一题-JS篇-根据name获取cookie中值的方法:https://github.com/LinDaiDai/niubility-coding-js/blob/master/%E6%AF%8F%E6%97%A5%E4%B8%80%E9%A2%98/%E6%AF%8F%E6%97%A5%E4%B8%80%E9%A2%98-JS%E7%AF%87.md#%E7%94%A8%E6%AD%A3%E5%88%99%E5%86%99%E4%B8%80%E4%B8%AA%E6%A0%B9%E6%8D%AEname%E8%8E%B7%E5%8F%96cookie%E4%B8%AD%E7%9A%84%E5%80%BC%E7%9A%84%E6%96%B9%E6%B3%95

7. 正则里的非如何实现的

^
要是放在 []
里的话就表示 "除了^后面的内容都能匹配"
,也就是非的意思。
例如:

(除了 l
,其它都变成了 "帅"
)

var str = 'lindaidai';
console.log(str.replace(/[^l]/g, '帅'));
// l帅帅帅帅帅帅帅帅

反之,如果是不在 []
里的话则表示开头匹配:

(只有 l
变成了 "帅"
)

var str = 'lindaidai';
console.log(str.replace(/^l/g, '帅'));

8. webpack几种hash的实现原理

  • hash
    hash
    hash
    
  • chunkhash
    chunk
    hash
    chunk
    hash
    chunk
    entry
    
  • contenthash
    hash
    hash
    

(具体可以看我简书上的这篇文章:https://www.jianshu.com/p/486453d81088)

这里只是说明了三种 hash
的不同…至于原理暂时没了解。

9. webpack如果使用了hash命名,那是每次都会重写生成hash吗

这个问题在上一个问题中已经说明了,要看 webpack
的配置。
有三种情况:

  • hash
    hash
    hash
    
  • chunkhash
    entry
    chunk
    chunk
    hash
    
  • 如果是 contenthash
    的话,和每个生成的文件有关,只有当要构建的文件内容发生改变时才会给该文件生成新的 hash
    值,并不会影响其它文件。

10. webpack中如何处理图片的?

webpack
中有两种处理图片的 loader

  • file-loader
    CSS
    url
    import/require()
    
  • url-loader
    limit
    url-loader
    base64
    url-loader
    base64
    http
    file-loader
    

详细使用可以查看这里:霖呆呆的webpack之路-loader篇:https://github.com/LinDaiDai/niubility-coding-js/blob/master/%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%8C%96/webpack/%E9%9C%96%E5%91%86%E5%91%86%E7%9A%84webpack%E4%B9%8B%E8%B7%AF-loader%E7%AF%87.md#file-loader

11. 说一下回流和重绘

「回流」:

触发条件:

当我们对 DOM 结构的修改引发 DOM 几何尺寸变化的时候,会发生 回流
的过程。
例如以下操作会触发回流:

  1. 一个 DOM 元素的几何属性变化,常见的几何属性有 width
    height
    padding
    margin
    left
    top
    border
    等等, 这个很好理解。

  2. 使 DOM 节点发生 增减
    或者 移动

  3. 读写 offset
    族、 scroll
    族和 client
    族属性的时候,浏览器为了获取这些值,需要进行回流操作。

  4. 调用 window.getComputedStyle
    方法。

回流过程:由于DOM的结构发生了改变,所以需要从生成DOM这一步开始,重新经过 样式计算
生成布局树
建立图层树
、再到 生成绘制列表
以及之后的显示器显示这整一个渲染过程走一遍,开销是非常大的。

「重绘」:

触发条件:

当 DOM 的修改导致了样式的变化,并且没有影响几何属性的时候,会导致 重绘
( repaint
)。

重绘过程:由于没有导致 DOM 几何属性的变化,因此元素的位置信息不需要更新,所以当发生重绘的时候,会跳过 生存布局树
建立图层树
的阶段,直接到 生成绘制列表
,然后继续进行分块、生成位图等后面一系列操作。

「如何避免触发回流和重绘」:

  1. 避免频繁使用 style,而是采用修改 class
    的方式。
  2. position
    absolute
    fixed
    
  3. display: none
    display
    none
    
  4. 使用 createDocumentFragment
    进行批量的 DOM 操作。
  5. 对于 resize、scroll 等进行防抖/节流处理。
  6. 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
  7. transform
    opacity
    filter
    CPU
    

参考来源:https://juejin.im/post/5df5bcea6fb9a016091def69#heading-57

12. 盒模型及如何转换

box-sizing: content-box
(W3C盒模型,又名标准盒模型):元素的宽高大小表现为内容的大小。

box-sizing: border-box
(IE盒模型,又名怪异盒模型):元素的宽高表现为内容 + 内边距 + 边框的大小。背景会延伸到边框的外沿。

13. 实现水平垂直居中的几种方式

这里我是按照子弈的总结答的:https://juejin.im/post/5d690c726fb9a06b155dd40d#heading-81

  • Flex布局(子元素是块级元素)
.box {
display: flex;
width: 100px;
height: 100px;
background-color: pink;
}

.box-center{
margin: auto;
background-color: greenyellow;
}
  • Flex布局
.box {
display: flex;
width: 100px;
height: 100px;
background-color: pink;
justify-content: center;
align-items: center;
}

.box-center{
background-color: greenyellow;
}
  • 绝对定位实现(定位元素定宽定高)
.box {
position: relative;
height: 100px;
width: 100px;
background-color: pink;
}

.box-center{
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
margin: auto;
width: 50px;
height: 50px;
background-color: greenyellow;
}

14. flex的兼容性怎样

「简单回答:」

IE6~9
不支持, IE10~11
部分支持 flex的2012版
,但是需要 -ms-
前缀。

其它的主流浏览器包括安卓和 IOS
基本上都支持了。

「详细回答:」

  • IE10部分支持2012,需要-ms-前缀
  • Android4.1/4.2-4.3部分支持2009
    ,需要-webkit-前缀

  • Safari7/7.1/8部分支持2012, 需要-webkit-前缀

  • IOS Safari7.0-7.1/8.1-8.3部分支持2012,需要-webkit-前缀

15. 你知道到哪里查看兼容性吗

可以到 Can I use
上去查看,官网地址为:https://caniuse.com/

16. 移动端中css你是使用什么单位

「比较常用的」:

  • em
    font-size: 14px
    font-size: 1em;
    font-size: 14px;
    14px
    width: 2em;
    width: 24px
    
  • rem
    html
    font-size: 14px
    1rem = 14px
    
  • %
    width: 200px
    width: 50%;height:50%;
    width: 100px;height: 100px;
    
  • vw和vh
    1000px
    60vw = 600px;
    
  • vmin和vmax
    vmin
    vw
    vh
    vmax
    375px
    812px
    100vmin = 375px;
    100vmax = 812px;
    

「不常用的:」

  • ex和ch
    ex
    "x"
    1ex
    "x"
    ch
    "0"
    2ch
    "0"
    

「移动端布局总结」:

  1. 移动端布局的方式主要使用rem和flex,可以结合各自的优点,比如flex布局很灵活,但是字体的大小不好控制,我们可以使用rem和媒体查询控制字体的大小,媒体查询视口的大小,然后不同的上视口大小下设置设置html的font-size。
  2. 可单独制作移动端页面也可响应式pc端移动端共用一个页面。没有好坏,视情况而定,因势利导

(总结来源:玲珑)

17. rem和em的区别

「em:」

定义字体大小时以父级的字体大小为基准;定义长度单位时以当前字体大小为基准。例父级 font-size: 14px
,则子级 font-size: 1em;
font-size: 14px;
;若定义长度时,子级的字体大小如果为 14px
,则子级 width: 2em;
width: 24px

「rem:」

以根元素的字体大小为基准。例如 html
font-size: 14px
,则子级 1rem = 14px

18. 在移动端中怎样初始化根元素的字体大小

一个简易版的初始化根元素字体大小。

页面开头处引入下面这段代码,用于动态计算 font-size

(假设你需要的 1rem = 20px
)

(function () {
var html = document.documentElement;
function onWindowResize() {
html.style.fontSize = html.getBoundingClientRect().width / 20 + 'px';
}
window.addEventListener('resize', onWindowResize);
onWindowResize();
})();
  • document.documentElement
    :获取 document
    的根元素
  • html.getBoundingClientRect().width
    :获取 html
    的宽度(窗口的宽度)
  • 监听 window
    resize
    事件

一般还需要配合一个 meta
头:

<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-sacle=1.0, maximum-scale=1.0, user-scalable=no" />

19. 移动端中不同手机html默认的字体大小都是一样的吗

如果没有人为去改变根元素字体大小的话,默认是 1rem = 16px
;根元素默认的字体大小是 16px

20. 你做过哪些动画效果

实话实说没太做过。

21. 如果让你实现一个一直旋转的动画你会如何做

css代码:

<style>
.box {
width: 100px;
height: 100px;
background-color: red;
animation: spin 2s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg) }
to { transform: rotate(360deg) }
}
</style>

html代码:

<div class="box"></div>

22. animation介绍一下

语法:

animation: name duration timing-function delay iteration-count direction;
描述
animation-name 规定需要绑定到选择器的 keyframe 名称。(mymove)
animation-duration 规定完成动画所花费的时间,以秒或毫秒计。(2s)
animation-timing-function 规定动画的速度曲线。(ease|linear|ease-in|cubic-bezier(n,n,n,n))
animation-delay 规定在动画开始之前的延迟。(2s)
animation-iteration-count 规定动画应该播放的次数。(n | infinite) n次/无限
animation-direction 规定是否应该轮流反向播放动画。(normal | alternate) 正常/反向

23. animation有一个steps()功能符知道吗?

一句话介绍: steps()
功能符可以让动画不连续。

地位和作用:和贝塞尔曲线( cubic-bezier()
修饰符)一样,都可以作为 animation
的第三个属性值。

和贝塞尔曲线的区别:贝塞尔曲线像是滑梯且有4个关键字(参数),而 steps
像是楼梯坡道且只有 number
position
两个关键字。
语法:

steps(number, position)
  • number: 数值,表示把动画分成了多少段
  • position: 表示动画是从时间段的开头连续还是末尾连续。支持 start
    end
    两个关键字,含义分别如下:
    • start
      :表示直接开始。
    • end
      :表示戛然而止。是默认值。

具体可以看这里:https://www.zhangxinxu.com/wordpress/2018/06/css3-animation-steps-step-start-end/

24. 用过哪些移动端的调试工具

  • Chrome
    more tools
    Remote devices
    chrome://inspect/#devices
    
  • Mac
    IOS
    Safari
    

25. 说一下原型链

26. 详细说一下instanceof

27. V8的垃圾回收是发生在什么时候?

V8引擎帮助我们实现了自动的垃圾回收管理, 「利用浏览器渲染页面的空闲时间进行垃圾回收。」

28. 具体说一下垃圾回收机制

(这里我用的是:https://juejin.im/post/5e8b261ae51d4546c0382ab4#heading-20 里的总结)

「栈内存的回收:」

栈内存调用栈上下文切换后就被回收,比较简单。

「堆内存的回收:」

V8的堆内存分为新生代内存和老生代内存,新生代内存是临时分配的内存,存在时间短,老生代内存存在时间长。
新生代内存回收机制:

  • 新生代内存容量小,64位系统下仅有32M。新生代内存分为 「From、To」
    两部分,进行垃圾回收时,先扫描From,将非存活对象回收,将存活对象顺序复制到To中,之后调换From/To,等待下一次回收

老生代内存回收机制

  • 「晋升」:如果新生代的变量经过多次回收依然存在,那么就会被放入老生代内存中

  • 「标记清除」:老生代内存会先遍历所有对象并打上标记,然后对正在使用或被强引用的对象取消标记,回收被标记的对象

  • 「整理内存碎片」:把对象挪到内存的一端

(当然想要详细了解的话也可以看我的这篇文章: JavaScript进阶-内存机制(表情包初探)

29. 在项目中如何把http的请求换成https

由于我在项目中是会对 axios
做一层封装,所以每次请求的域名也是写在配置文件中,有一个 baseURL
字段专门用于存储它,所以只要改这个字段就可以达到替换所有请求 http
https
了。
当然后面我也有了解到:

利用 meta
标签把 http
请求换为 https
:

<meta http-equiv ="Content-Security-Policy" content="upgrade-insecure-requests">

30. 知道meta标签有把http换成https的功能吗?

参考上一题:point_up_2:。

31. http请求可以怎么拦截

在浏览器和服务器进行传输的时候,可以被 nginx
代理所拦截,也可以被网关拦截。

32. https的加密方式

HTTPS主要是采用对称密钥加密和非对称密钥加密组合而成的混合加密机制进行传输。
也就是发送密文的一方用”对方的公钥”进行加密处理”对称的密钥”,然后对方在收到之后使用自己的私钥进行解密得到”对称的密钥”,这在确保双发交换的密钥是安全的前提下使用对称密钥方式进行通信。

33. 混合加密的好处

对称密钥加密和非对称密钥加密都有它们各种的优缺点,而混合加密机制就是将两者结合利用它们各自的优点来进行加密传输。
比如既然对称密钥的优点是加解密效率快,那么在客户端与服务端确定了连接之后就可以用它来进行加密传输。不过前提是得解决双方都能安全的拿到这把对称密钥。这时候就可以利用非对称密钥加密来传输这把对称密钥,因为我们知道非对称密钥加密的优点就是能保证传输的内容是安全的。
所以它的好处是即保证了对称密钥能在双方之间安全的传输,又能使用对称加密方式进行通信,这比单纯的使用非对称加密通信快了很多。以此来解决了HTTP中内容可能被窃听的问题。
其它HTTP相关的问题:
如:

  • HTTPS的工作流程
  • 混合加密机制的好处
  • 数字签名
  • ECDHE握手和RSA握手
  • 向前安全性

这些问题都可以看到我的这篇文章:HTTPS面试问答:https://github.com/LinDaiDai/niubility-coding-js/blob/master/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/HTTPS%E9%9D%A2%E8%AF%95%E9%97%AE%E7%AD%94.md

34. 浏览器如何验证服务器的身份

这道题主要可以从 数字签名
数字证书
上来答。

当时我答的时候就把 证书的颁发流程
HTTPS
数字证书的验证过程完整的说了一遍就算过了。

具体可以看「HTTPS面试」:point_up_2:问答中的第 5、6、7
问。

35. ETag首部字段说一下

这个无非就是配合 If-None-Match
来达到一个 协商缓存
的作用。值为服务器某个资源的唯一标识。
具体可以看我的这篇文章:霖呆呆你来说说浏览器缓存吧

36. 你们的token一般是存放在哪里的

Token
其实就是 「访问资源的凭证」

一般是用户通过用户名和密码登录成功之后,服务器将登陆凭证做数字签名,加密之后得到的字符串作为 token

它在用户登录成功之后会返回给客户端,客户端主要有这么几种存储方式:

  1. 存储在 localStorage
    中,每次调用接口的时候都把它当成一个字段传给后台
  2. 存储在 cookie
    中,让它自动发送,不过缺点就是不能跨域
  3. localStorage
    HTTP
    Authorization
    

(很明显我答的不够专业,欢迎补充,感谢:blush:)

37. token会不会被伪造?

(很明显我答的不够专业,欢迎补充,感谢:blush:)

38. redis中一般用来存什么

用户登录成功之后的一些信息
(很明显我答的不够专业,欢迎补充,感谢:blush:)

39. 前后端如何验证一个用户是否下线了

(很明显我答的不够专业,欢迎补充,感谢:blush:)

40. CSP白名单知道吗?

(很明显我答的不够专业,欢迎补充,感谢:blush:)

41. nginx有配置过吗?

只会配置一些跨域方面的问题。

events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://localhost:8887;
add_header Access-Control-Allow-Origin *;
}
}
}

利用ngnix跨域的关键就是在配置文件中设置 server
项,然后设置其中的 location
属性, proxy_pass
:需要代理的服务器地址,add_header:给响应报文中添加首部字段,例如 Access-Control-Allow-Origin
设置为 *
,即允许所有的请求源请求。

具体可以看:Yiming君-面试题:nginx有配置过吗?反向代理知道吗?
https://juejin.im/post/5eb0f8aaf265da7ba65f7ba2

42. 反向代理知道吗?

我们将请求发送到服务器,然后服务器对我们的请求进行转发,我们只需要和代理服务器进行通信就好。所以对于客户端来说,是感知不到服务器的。

43. 有用过抓包工具吗?

唔…没有…

44. 你平常用的电脑是Mac吗?

(…在电话那头默默的点头)

45. Fiddler有用过吗?

唔…知道…

46. Vue的diff算法

这里我是按照:
https://mp.weixin.qq.com/s/2xyP4jVevuXrGov_VsWfvA
中的第13题答的。

47. Vue中computed和methods的区别

48. 例如要获取当前时间你会放到computed还是methods里?

放在 methods
中,因为 computed
会有惰性,并不能知道 new Date()
的改变。

49. 你们的权限功能是怎么做的?

小小的写了一篇文章,可以看这里:数据权限如何控制
https://github.com/LinDaiDai/niubility-coding-js/blob/master/other/%E6%95%B0%E6%8D%AE%E6%9D%83%E9%99%90%E5%A6%82%E4%BD%95%E6%8E%A7%E5%88%B6.md

50. 那你在判断权限的时候是用的字符串匹配还是位运算?

和面试官扯了一堆我数据权限判断的具体过程,其中可能有多个权限:并的情况 000011110001&000011110002
,或的情况 000011110001|000011110002
,以及如何做的权限匹配。最后面试官:

"所以那还是用的字符串匹配咯?"

尬…我比较 low
…用的字符串匹配…
(哇,真的绝了…1个小时20分钟50多道题,答的我口渴:joy:,不过也可以看出有很多移动端的我都没有答上来,面试官也表示理解,毕竟我主要是以PC端为主,所以竟然也算是过了,很感谢这位面试官细心的帮我分析一些问题)
后来有了解这位面试官近期也跳槽去腾讯了,果然面完呆呆之后他就去大厂了,好人一生平安:joy::pray:。

深圳某国内直播公司

这家公司其实是上家公司的总部,因为面完上家之后,HR也知道我的顾虑,想要去一个大点的团队,所以就把我推荐去了他们的总部。非常Nice的HR小姐姐,好感动,就算你看不到我的文章,我也还是要感谢你 :joy:。
5月8日
一面(前端副总监)

1. 输入URL到页面呈现

(必问…)

看三元的《(1.6w字)浏览器灵魂之问,请问你能接得住几个?》
https://juejin.im/post/5df5bcea6fb9a016091def69
分别从网络,解析,渲染来说

2. 为什么说script标签建议放在body下面?

JS
代码在加载完之后是立即执行的,且 JS
代码执行时会阻塞页面的渲染。

3. 为什么说script标签会阻塞页面的渲染呢?渲染线程和js引擎线程不是分开的吗?

JS属于单线程,当我们在加载 script
标签内容的时候,渲染线程会被暂停,因为 script
标签里可能会操作 DOM
的,所以如果你加载 script
标签又同时渲染页面肯定就冲突了,因此说渲染线程( GUI
)和js引擎线程互斥。

4. 协商缓存说一下

Last-Modefied
配合 If-Modified-Since

ETag
配合 If-None-Match

也是个常见的问题了,不了解的小伙伴可以看我的这篇文章: 霖呆呆你来说说浏览器缓存吧

(当时面试官还重复了一下我说的这4个头部字段,自己回顾了一下我说的对不对,好可爱~)

5. HTTP中的Keep-Alive有了解过吗?

Keep-Alive
HTTP
的一个头部字段 Connection
中的一个值,它是保证我们的 HTTP
请求能建立一个持久连接。也就是说建立一次 TCP
连接即可进行多次请求和响应的交互。它的特点就是只要有一方没有明确的提出断开连接,则保持 TCP
连接状态,减少了 TCP
连接和断开造成的额外开销。

另外,在 HTTP/1.1
中所有的连接默认都是持久连接的,但是 HTTP/1.0
并未标准化。

6. 跨域有了解吗?如何解决跨域?

我工作中碰到主要是利用 CORS
来解决跨域问题,说了一下它的原理以及后台需要如何做。

另外说到了 JSONP
的原理,以及它的优点:兼容性好;缺点:只能进行 GET
请求,且有安全问题。

还有说到了 ngnix
反向代理来解决跨域。

  • CORS原理及实现:https://www.jianshu.com/p/b2bdf55e1bf5
  • JSONP原理及实现:https://www.jianshu.com/p/88bb82718517
  • 面试题:nginx有配置过吗?反向代理知道吗?:https://juejin.im/post/5eb0f8aaf265da7ba65f7ba2

其它的,我当时说我有看过一篇文章里面详细的介绍10多种跨域解决方案,但是自己没有过多的去了解。
哈哈,其实也就是秋风大大的这篇文章10种跨域解决方案(附终极大招)

7. WebSocket有了解过吗?它也可以跨域的

这个当时答的没用过。
我知道它是能使得客户端和服务器之间存在持久的连接,而且双方都可以随时开始发送数据,这种方式本质没有使用 HTTP 的响应头,因此也没有跨域的限制。
(多的不会了)

8. 前端安全方面?XSS?CSRF?

(必问…)
(以下回答参考子弈小哥哥的面试分享:两年工作经验成功面试阿里P6总结:https://juejin.im/post/5d690c726fb9a06b155dd40d#heading-60
以及蔡徐坤小哥哥的2万字 | 前端基础拾遗90问:https://juejin.im/post/5e8b261ae51d4546c0382ab4#heading-48

「XSS」

XSS(Cross Site Script)跨站脚本攻击。指的是攻击者向网页注入恶意的客户端代码,通过恶意的脚本对客户端网页进行篡改,从而在用户浏览网页时,对用户浏览器进行控制或者获取用户隐私数据的一种攻击方式。
主要是分为三种:

「存储型」:即攻击被存储在服务端,常见的是在评论区插入攻击脚本,如果脚本被储存到服务端,那么所有看见对应评论的用户都会受到攻击。

「反射型」:攻击者将脚本混在URL里,服务端接收到URL将恶意代码当做参数取出并拼接在HTML里返回,浏览器解析此HTML后即执行恶意代码

「DOM型」:将攻击脚本写在URL中,诱导用户点击该URL,如果URL被解析,那么攻击脚本就会被运行。和前两者的差别主要在于DOM型攻击不经过服务端
如何防御XSS攻击

  • 「输入检查」
    :对输入内容中的 script

    等标签进行转义或者过滤
  • 「设置httpOnly」:很多XSS攻击目标都是窃取用户cookie伪造身份认证,设置此属性可防止JS获取cookie

  • 「开启CSP」,即开启白名单,可阻止白名单以外的资源加载和运行

「CSRF」

CSRF攻击(Cross-site request forgery)跨站请求伪造。是一种劫持受信任用户向服务器发送非预期请求的攻击方式,通常情况下,它是攻击者借助受害者的 Cookie 骗取服务器的信任,但是它并不能拿到Cookie,也看不到Cookie的内容,它能做的就是给服务器发送请求,然后执行请求中所描述的命令,以此来改变服务器中的数据,也就是并不能窃取服务器中的数据。
防御主要有三种:

验证 Token
:浏览器请求服务器时,服务器返回一个token,每个请求都需要同时带上token和cookie才会被认为是合法请求

验证 Referer
:通过验证请求头的Referer来验证来源站点,但请求头很容易伪造

设置 SameSite
:设置cookie的SameSite,可以让cookie不随跨站请求发出,但浏览器兼容不一

「点击挟持」

  • 诱使用户点击看似无害的按钮(实则点击了透明 iframe 中的按钮).
  • 监听鼠标移动事件,让危险按钮始终在鼠标下方.
  • 使用 HTML5 拖拽技术执行敏感操作(例如 deploy key).

预防策略:

  1. 服务端添加 X-Frame-Options 响应头,这个 HTTP 响应头是为了防御用 iframe 嵌套的点击劫持攻击。这样浏览器就会阻止嵌入网页的渲染。
  2. JS 判断顶层视口的域名是不是和本页面的域名一致,不一致则不允许操作, top.location.hostname === self.location.hostname
  3. 敏感操作使用更复杂的步骤(验证码、输入项目名称以删除)。

(这个来源于LuckyWinty: http://www.imooc.com/article/295400)

9. setTimeout的执行原理(EventLoop)

(必问…)
(回答参考:https://juejin.im/post/5e621f5fe51d452700567c32)

setTimeout
的运行机制:执行该语句时,是立即把当前定时器代码推入事件队列,当定时器在事件列表中满足设置的时间值时将传入的函数加入任务队列,之后的执行就交给任务队列负责。但是如果此时任务队列不为空,则需等待,所以执行定时器内代码的时间可能会大于设置的时间

说了一下它属于异步任务,然后说了一下还有哪些宏任务以及微任务,最后说了一下 EventLoop
的执行过程。

  • 一开始整个脚本作为一个宏任务执行
  • 执行过程中同步代码直接执行,宏任务进入宏任务队列,微任务进入微任务队列
  • 当前宏任务执行完出队,检查微任务列表,有则依次执行,直到全部执行完
  • 执行浏览器UI线程的渲染工作
  • 检查是否有 Web Worker
    任务,有则执行

  • 执行完本轮的宏任务,回到2,依此循环,直到宏任务和微任务队列都为空

(具体可以看这里:https://juejin.im/post/5e58c618e51d4526ed66b5cf#heading-1)

10. requestAnimationFrame有了解过吗?

(啪啪啪,不长记性,其实之前面试有被问过,但是忘了再去了解了,这就吃亏了,没答上来)

requestAnimationFrame
是浏览器用于定时循环操作的一个接口,类似于 setTimeout
,主要用途是按帧对网页进行重绘。对于 JS
动画,用 requestAnimationFrame
会比 setInterval
效果更好。
具体可以看:https://juejin.im/post/5e621f5fe51d452700567c32

11. requestAnimationFrame和setTimeout的区别?

同上…

12. 平常工作中ES6+主要用到了哪些?

(下面看着很多,但我肯定不是全答哈,挑了几个来回答)

ES6

  1. Class

  2. 模块 import
    export

  3. 箭头函数
  4. 函数默认参数
  5. ...
    扩展运输符允许展开数组

  6. 解构
  7. 字符串模版
  8. Promise
  9. let const

  10. Proxy、Map、Set

  11. 对象属性同名能简写

ES7

  1. includes

  2. **
    求幂运算符

ES8

  1. async/await
  2. Object.values()和Object.entries()
  3. padStart()和padEnd()
  4. Object.getOwnPropertyDescriptors()
  5. 函数参数允许尾部 ,

ES9

  1. for...await...of
  2. ...
    展开符合允许展开对象收集剩余参数
  3. Promise.finally()
  4. 正则中的四个新功能

ES10

  1. flat()
  2. flatMap()
  3. fromEntries()
  4. trimStart
    trimEnd
  5. matchAll
  6. BigInt
  7. try/catch
    中报错允许没有 err
    异常参数
  8. Symbol.prototype.description
  9. Function.toString()
    调用时呈现原本源码的样子

还不了解的小伙伴可以看看浪里哥的这篇:盘点ES7、ES8、ES9、ES10新特性
https://juejin.im/post/5dda2b5e6fb9a07a83691766

13. 如何在前端实现一个图片压缩

实话实话没做过,但是后来面试官告诉我:可以使用 canvas
来实现。具体做法等我写篇文章哈。
(当时我还反问了一句面试官:那批量图片压缩要怎么做呢?把他惊的…然后他和我说挺复杂的…)

14. 你上家公司主要是做什么的?

15. 团队多少人呢?

1个前端(我),1个小程序老哥(IOS转行的),6个后台。

16. 项目中有碰到什么难的问题吗?如何解决的?

例举了我最经典的 bpmn.js
,以此来引出我写了很多关于这方面的教材,以及建立了微信群,为国内的 bpmn.js
社区贡献了一份力量…怎么高大上怎么来…

当然也有提到我 GitHub
上的 bpmn-chinese-document
项目只有 100
多的 Star
,他说理解,毕竟这东西用的人不是很多。

17. 期望薪资多少?

喊了个挺高的数,老哥笑了笑,你这个工作年限我们可能给不到,然后扯了点别的。

18. 还有什么想要问我的吗?

  • 团队人员分布情况
  • 技术栈
  • 我进去主要是负责哪块内容
  • 年终奖/季度奖
  • 调薪的频率以及幅度
  • 加班多不多

(其实后面这些问题应该是等到HR面的时候问的,但是感觉和面试官挺聊的来的我就先打听了)
二面(CTO)

1. JSONP的实现原理

绝了…又来

2. XSS攻击以及如何预防?

绝了…又来X2

3. 不使用框架如何实现组件按需加载以及原理

当时答的是是用 import
来按需引入,以及提到了 Vue.use

但后来有去了解, babel-plugin-import
就可以实现。

4. 你们这个是自己写的组件库吗?

估计面试官看错了…虽然我的项目有个组件库的功能,但是是基于 Ant Design of Vue
二次开发的。

5. 还有什么想要问我的吗??

没了…
三面(HR)
问的问题有点多,我挑一些记得住的哈

1. 第一家公司为什么离职?第二家为什么离职?

2. 第一家工资多少?第二家多少?

3. 两家公司主要是做什么的?规模是多大?

4. 之前都是你一个前端吗?

5. 有了解过我们公司吗?感觉怎么样?

6. 因为我们现在整个研发团队人也不是太多就30多个,前端加上总监可能就4个,你会考虑这么一个团队吗?

7. 有拿到其它的offer吗?

8. 拒绝一些offer的原因是什么?

9. 你的期望薪资我们可能给不到,你怎么想的?

10. 平常的兴趣爱好是什么?

11. 老家在哪里?…

12. 现在住哪里?…

13. 还有什么想要问我的吗???

面试推荐好文

  • 面试分享:两年工作经验成功面试阿里P6总结:https://juejin.im/post/5d690c726fb9a06b155dd40d
  • 2万字 | 前端基础拾遗90问:https://juejin.im/post/5e8b261ae51d4546c0382ab4
  • 一位前端小姐姐的五万字面试宝典https://juejin.im/post/5e91b01651882573716a9b23
  • 艺术喵 2 年前端面试心路历程(字节跳动、YY、虎牙、BIGO)| 掘金技术征文:https://juejin.im/post/5e85ec79e51d4547153d0738
  • 一年半经验前端社招7家大厂&独角兽全过经历 | 掘金技术征文:https://juejin.im/post/5e8d5a48f265da47ce6cb21f
  • 面试完50个人后我写下这篇总结:https://juejin.im/post/5df1e312f265da33d039d06d
  • 「吐血整理」再来一打Webpack面试题(持续更新)

  • (1.6w字)浏览器灵魂之问,请问你能接得住几个?: https://juejin.im/post/5df5bcea6fb9a016091def69


了方便进行探讨和交流,我为大家建立了一个读者群,一起学习,一起进步。

:heart:爱心三连击

1.看到这里了就点个在看支持下吧,你的 「在看」
是我创作的动力。

2.关注公众号 达达前端
「每天为您分享原创或精选文章」

3.特殊阶段,带好口罩,做好个人防护。

4.添加微信【xiaoda0423】,拉你进 技术交流群
一起学习
扫码关注公众号,订阅更多精彩内容。

好文章,我

在看
:heart:

相关推荐:

《全网最详bpmn.js教材》

《【建议改成】读完这篇你还不懂Babel我给你寄口罩》

《【建议星星】要就来45道Promise面试题一次爽到底(1.1w字用心整理)》

《【建议:+1:】再来40道this面试题酸爽继续(1.2w字用手整理)》

《【何不三连】比继承家业还要简单的JS继承题-封装篇(牛刀小试)》

《【何不三连】做完这48道题彻底弄懂JS继承(1.7w字含辛整理-返璞归真)》

《【精】从206个console.log()完全弄懂数据类型转换的前世今生(上)》