【半月刊 3】前端高频面试题及答案汇总

更新:谢谢你们的支持,最近折腾了一个博客官网出来,方便你们系统阅读,后续会有更多内容和更多优化,猛戳这里查看html

------ 如下是正文 ------前端

引言

半月刊第三期到来,这段时间 Daily-Interview-Question 新增了 15 道高频面试题,今天就把最近半月汇总的面试题和部分答案发给你们,帮助你们查漏补缺。html5

更多更全的面试题和答案在下面的项目地址中,点击查看。node

项目地址:Daily-Interview-Questionwebpack

第 25 题:说说浏览器和 Node 事件循环的区别

浏览器

关于微任务和宏任务在浏览器的执行顺序是这样的:git

  • 执行一个task(宏任务)
  • 执行完micro-task队列 (微任务)

如此循环往复下去github

浏览器的 task(宏任务)执行顺序在 html#event-loops 里面有讲就不翻译了 常见的 task(宏任务) 好比:setTimeout、setInterval、script(总体代码)、 I/O 操做、UI 渲染等。 常见的 micro-task 好比: new Promise().then(回调)、MutationObserver(html5新特性) 等。web

Node

Node的事件循环是libuv实现的,引用一张官网的图:面试

default

大致的task(宏任务)执行顺序是这样的:chrome

  • timers定时器:本阶段执行已经安排的 setTimeout() 和 setInterval() 的回调函数。
  • pending callbacks待定回调:执行延迟到下一个循环迭代的 I/O 回调。
  • idle, prepare:仅系统内部使用。
  • poll 轮询:检索新的 I/O 事件;执行与 I/O 相关的回调(几乎全部状况下,除了关闭的回调函数,它们由计时器和 setImmediate() 排定的以外),其他状况 node 将在此处阻塞。
  • check 检测:setImmediate() 回调函数在这里执行。
  • close callbacks 关闭的回调函数:一些准备关闭的回调函数,如:socket.on('close', ...)。

微任务和宏任务在Node的执行顺序

Node 10之前:

  • 执行完一个阶段的全部任务
  • 执行完nextTick队列里面的内容
  • 而后执行完微任务队列的内容

Node 11之后: 和浏览器的行为统一了,都是每执行一个宏任务就执行完微任务队列。

未完待续,点击查看更多细节:第25题:浏览器和Node 事件循环的区别

第 26 题:介绍模块化发展历程

可从IIFE、AMD、CMD、CommonJS、UMD、webpack(require.ensure)、ES Module、<script type="module"> 这几个角度考虑。

解答:

模块化主要是用来抽离公共代码,隔离做用域,避免变量冲突等。

IIFE: 使用自执行函数来编写模块化,特色:在一个单独的函数做用域中执行代码,避免变量冲突

(function(){
  return {
	data:[]
  }
})()
复制代码

AMD: 使用requireJS 来编写模块化,特色:依赖必须提早声明好

define('./index.js',function(code){
	// code 就是index.js 返回的内容
})
复制代码

CMD: 使用seaJS 来编写模块化,特色:支持动态引入依赖文件

define(function(require, exports, module) {  
  var indexCode = require('./index.js');
});
复制代码

CommonJS: nodejs 中自带的模块化。

var fs = require('fs');
复制代码

UMD:兼容AMD,CommonJS 模块化语法。

webpack(require.ensure):webpack 2.x 版本中的代码分割。

ES Modules: ES6 引入的模块化,支持import 来引入另外一个 js 。

import a from 'a';
复制代码

未完待续,点击查看更多细节:第26 题: 前端中的模块化开发

第 27 题:全局做用域中,用 const 和 let 声明的变量不在 window 上,那到底在哪里?如何去获取?。

在ES5中,顶层对象的属性和全局变量是等价的,var 命令和 function 命令声明的全局变量,天然也是顶层对象。

var a = 12;
function f(){};

console.log(window.a); // 12
console.log(window.f); // f(){}
复制代码

但ES6规定,var 命令和 function 命令声明的全局变量,依旧是顶层对象的属性,但 let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。

let aa = 1;
const bb = 2;

console.log(window.aa); // undefined
console.log(window.bb); // undefined
复制代码

在哪里?怎么获取?经过在设置断点,看看浏览器是怎么处理的:

letandconst

经过上图也能够看到,在全局做用域中,用 let 和 const 声明的全局变量并无在全局对象中,只是一个块级做用域(Script)中

怎么获取?在定义变量的块级做用域中就能获取啊,既然不属于顶层对象,那就不加 window(global)呗。

let aa = 1;
const bb = 2;

console.log(aa); // 1
console.log(bb); // 2
复制代码

未完待续,点击查看更多细节:第27题:关于 const 和 let 声明的变量不在 window 上

第 28 题:cookie 和 token 都存放在 header 中,为何不会劫持 token?

一、首先token不是防止XSS的,而是为了防止CSRF的; 二、CSRF攻击的缘由是浏览器会自动带上cookie,而浏览器不会自动带上token

未完待续,点击查看更多细节:第28题:cookie 和 token 都存放在 header 中,为何不会劫持 token?

第 29 题:聊聊 Vue 的双向数据绑定,Model 如何改变 View,View 又是如何改变 Model 的

img

VM 主要作了两件微小的事情:

  • 从 M 到 V 的映射(Data Binding),这样能够大量节省你人肉来 update View 的代码
  • 从 V 到 M 的事件监听(DOM Listeners),这样你的 Model 会随着 View 触发事件而改变

一、M 到 V 实现

作到这件事的第一步是造成相似于:

// template
var tpl = '<p>{{ text }}</p>';
// data
var data = {
text: 'This is some text'
};
// magic process
template(tpl, data); // '<p>This is some text</p>'
复制代码

中间的 magic process 是模板引擎所作的事情,已经有很是多种模板引擎可供选择

固然你比较喜欢造轮子的话也能够本身实现一个

不管是 Angular 的 $scope,React 的 state 仍是 Vue 的 data 都提供了一个较为核心的 model 对象用来保存模型的状态;它们的模板引擎稍有差异,不过大致思路类似;拿到渲染后的 string 接下来作什么不言而喻了(中间还有不少处理,例如利用 model 的 diff 来最小量更新 view )。

可是仅仅是这样并不够,咱们须要知道何时来更新 view( 即 render ),通常来讲主要的 VM 作了如下几种选择:

  • VM 实例初始化时
  • model 动态修改时

其中初始化拿到 model 对象而后 render 没什么好讲的;model 被修改的时候如何监听属性的改变是一个问题,目前有如下几种思路:

  • 借助于 Object 的 observe 方法
  • 本身在 set,以及数组的经常使用操做里触发 change 事件
  • 手动 setState(),而后在里面触发 change 事件

知道了触发 render 的时机以及如何 render,一个简单的 M 到 V 映射就实现了。

二、V 到 M 实现

从 V 到 M 主要由两类( 虽然本质上都是监听 DOM )构成,一类是用户自定义的 listener, 一类是 VM 自动处理的含有 value 属性元素的 listener

第一类相似于你在 Vue 里用 v-on 时绑定的那样,VM 在实例化得时候能够将全部用户自定义的 listener 一次性代理到根元素上,这些 listener 能够访问到你的 model 对象,这样你就能够在 listener 中改变 model

第二类相似于对含有 v-model 与 value 元素的自动处理,咱们指望的是例如在一个输入框内

<input type="text" v-model="message" />
复制代码

输入值,那么我与之对应的 model 属性 message 也会随之改变,至关于 VM 作了一个默认的 listener,它会监听这些元素的改变而后自动改变 model,具体如何实现相信你也明白了

参考地址:mvvm 中 viewmodel该如何设计?

未完待续,点击查看更多细节:第29题

第 30 题:两个数组合并成一个数组

请把两个数组 ['A1', 'A2', 'B1', 'B2', 'C1', 'C2', 'D1', 'D2'] 和 ['A', 'B', 'C', 'D'],合并为 ['A1', 'A2', 'A', 'B1', 'B2', 'B', 'C1', 'C2', 'C', 'D1', 'D2', 'D']。

解法:

let a1 =  ['A1', 'A2', 'B1', 'B2', 'C1', 'C2', 'D1', 'D2']
let a2 = ['A', 'B', 'C', 'D'].map((item) => {
  return item + 3
})

let a3 = [...a1, ...a2].sort().map((item) => {
  if(item.includes('3')){
    return item.split('')[0]
  }
  return item
})
复制代码

未完待续,点击查看更多细节: 第 30 题

第 31 题:改造下面的代码,使之输出0 - 9,写出你能想到的全部解法。

for (var i = 0; i< 10; i++){
	setTimeout(() => {
		console.log(i);
    }, 1000)
}
复制代码

解法1:

for (var i = 0; i< 10; i++){
   setTimeout((i) => {
   	  console.log(i);
   }, 1000,i)
}
复制代码

解法2:

for (var i = 0; i< 10; i++){
  ((i) => {
    setTimeout(() => {
      console.log(i);
    }, 1000)
 })(i)
}
复制代码

解法3:

for (let i = 0; i< 10; i++){
  setTimeout(() => {
    console.log(i);
  }, 1000)
}
复制代码

未完待续,点击查看更多细节:第 31 题

第 32 题:Virtual DOM 真的比操做原生 DOM 快吗?谈谈你的想法。

采用尤大大的回答:

1. 原生 DOM 操做 vs. 经过框架封装操做。

这是一个性能 vs. 可维护性的取舍。框架的意义在于为你掩盖底层的 DOM 操做,让你用更声明式的方式来描述你的目的,从而让你的代码更容易维护。没有任何框架能够比纯手动的优化 DOM 操做更快,由于框架的 DOM 操做层须要应对任何上层 API 可能产生的操做,它的实现必须是普适的。针对任何一个 benchmark,我均可以写出比任何框架更快的手动优化,可是那有什么意义呢?在构建一个实际应用的时候,你难道为每个地方都去作手动优化吗?出于可维护性的考虑,这显然不可能。框架给你的保证是,你在不须要手动优化的状况下,我依然能够给你提供过得去的性能。

2. 对 React 的 Virtual DOM 的误解。

React 历来没有说过 “React 比原生操做 DOM 快”。React 的基本思惟模式是每次有变更就整个从新渲染整个应用。若是没有 Virtual DOM,简单来想就是直接重置 innerHTML。不少人都没有意识到,在一个大型列表全部数据都变了的状况下,重置 innerHTML 实际上是一个还算合理的操做... 真正的问题是在 “所有从新渲染” 的思惟模式下,即便只有一行数据变了,它也须要重置整个 innerHTML,这时候显然就有大量的浪费。

咱们能够比较一下 innerHTML vs. Virtual DOM 的重绘性能消耗:

  • innerHTML: render html string O(template size) + 从新建立全部 DOM 元素 O(DOM size)
  • Virtual DOM: render Virtual DOM + diff O(template size) + 必要的 DOM 更新 O(DOM change)

Virtual DOM render + diff 显然比渲染 html 字符串要慢,可是!它依然是纯 js 层面的计算,比起后面的 DOM 操做来讲,依然便宜了太多。能够看到,innerHTML 的总计算量无论是 js 计算仍是 DOM 操做都是和整个界面的大小相关,但 Virtual DOM 的计算量里面,只有 js 计算和界面大小相关,DOM 操做是和数据的变更量相关的。前面说了,和 DOM 操做比起来,js 计算是极其便宜的。这才是为何要有 Virtual DOM:它保证了 1)无论你的数据变化多少,每次重绘的性能均可以接受;2) 你依然能够用相似 innerHTML 的思路去写你的应用。

3. 性能比较也要看场合

在比较性能的时候,要分清楚初始渲染、小量数据更新、大量数据更新这些不一样的场合。Virtual DOM、脏检查 MVVM、数据收集 MVVM 在不一样场合各有不一样的表现和不一样的优化需求。Virtual DOM 为了提高小量数据更新时的性能,也须要针对性的优化,好比 shouldComponentUpdate 或是 immutable data。

  • 初始渲染:Virtual DOM > 脏检查 >= 依赖收集
  • 小量数据更新:依赖收集 >> Virtual DOM + 优化 > 脏检查(没法优化) > Virtual DOM 无优化
  • 大量数据更新:脏检查 + 优化 >= 依赖收集 + 优化 > Virtual DOM(没法/无需优化)>> MVVM 无优化

不要天真地觉得 Virtual DOM 就是快,diff 不是免费的,batching 么 MVVM 也能作,并且最终 patch 的时候还不是要用原生 API。在我看来 Virtual DOM 真正的价值历来都不是性能,而是它 1) 为函数式的 UI 编程方式打开了大门;2) 能够渲染到 DOM 之外的 backend,好比 ReactNative。

未完待续,点击查看更多细节:第 32 题

第 33 题:下面的代码打印什么内容,为何?

var b = 10;
(function b(){
    b = 20;
    console.log(b); 
})();
复制代码

解答:

  1. 函数表达式与函数声明不一样,函数名只在该函数内部有效,而且此绑定是常量绑定。
  2. 对于一个常量进行赋值,在 strict 模式下会报错,非 strict 模式下静默失败。
  3. IIFE中的函数是函数表达式,而不是函数声明。

实际上,有点相似于如下代码,但不彻底相同,由于使用const无论在什么模式下,都会TypeError类型的错误

const foo = function () {
  foo = 10;
  console.log(foo)
}
(foo)() // Uncaught TypeError: Assignment to constant variable.
复制代码

b 函数是一个至关于用const定义的常量,内部没法进行从新赋值,若是在严格模式下,会报错"Uncaught TypeError: Assignment to constant variable." 例以下面的:

var b = 10;
(function b() {
 'use strict'
  b = 20;
  console.log(b)
})() // "Uncaught TypeError: Assignment to constant variable."
复制代码

未完待续,点击查看更多细节:第 33 题

第 34 题:简单改造下面的代码,使之分别打印 10 和 20。

var b = 10;
(function b(){
    b = 20;
    console.log(b); 
})();
复制代码

解法:

分åˆ

未完待续,点击查看更多细节:第 34 题

第 35 题:浏览器缓存读取规则

能够分红 Service Worker、Memory Cache、Disk Cache 和 Push Cache,那请求的时候 from memory cache 和 from disk cache 的依据是什么,哪些数据何时存放在 Memory Cache 和 Disk Cache中?

解答:

总的来讲:

  1. 若是开启了Service Worker首先会从Service Worker中拿
  2. 若是新开一个之前打开过的页面缓存会从Disk Cache中拿(前提是命中强缓存)
  3. 刷新当前页面时浏览器会根据当前运行环境内存来决定是从 Memory Cache 仍是 从Disk Cache中拿(能够看到下图最后几个文件有时候是从 Memory Cache中拿有时候是从Disk Cache中拿)
    这是个人实验过程

注意:以上回答所有基于chrome浏览器

未完待续,点击查看更多细节:第 35 题

第 36 题:使用迭代的方式实现 flatten 函数。

迭代的实现:

let arr = [1, 2, [3, 4, 5, [6, 7], 8], 9, 10, [11, [12, 13]]]

const flatten = function (arr) {
    while (arr.some(item => Array.isArray(item))) {
        arr = [].concat(...arr)
    }
    return arr
}

console.log(flatten(arr))
复制代码

递归的实现(ES6简写):

const flatten = array => array.reduce((acc, cur) => (Array.isArray(cur) ? [...acc, ...flatten(cur)] : [...acc, cur]), [])
复制代码

未完待续,点击查看更多细节:第 36 题

第 37 题:为何 Vuex 的 mutation 和 Redux 的 reducer 中不能作异步操做?

欢迎在 Issue 区留下你的答案。

第 38 题:下面代码中 a 在什么状况下会打印 1?

var a = ?;
if(a == 1 && a == 2 && a == 3){
 	console.log(1);
}
复制代码

解法1:利用 toString

let a = {
  i: 1,
  toString () {
    return a.i++
  }
}

if(a == 1 && a == 2 && a == 3) {
  console.log(1);
}
复制代码

解法2:利用 valueOf

let a = {
  i: 1,
  valueOf () {
    return a.i++
  }
}

if(a == 1 && a == 2 && a == 3) {
  console.log(1);
}
复制代码

解法3:数组这个就有点妖了

var a = [1,2,3];
a.join = a.shift;
if(a == 1 && a == 2 && a == 3) {
  console.log(1);
}
复制代码

解法4:ES6的symbol

let a = {
    [Symbol.toPrimitive]: (i => () => ++i) (0)
};
if(a == 1 && a == 2 && a == 3) {
  console.log(1);
}
复制代码

解法5:Object.defineProperty

Object.defineProperty(window, 'a', {
    get: function() {
          return this.value = this.value ? (this.value += 1) : 1;
    }
});
if(a == 1 && a == 2 && a == 3) {
  console.log(1);
}
复制代码

未完待续,点击查看更多细节:第 38 题

第 39 题:介绍下 BFC 及其应用。

BFC是CSS布局的一个概念,是一块独立的渲染区域,是一个环境,里面的元素不会影响到外部的元素 。

  • 如何生成BFC:(即脱离文档流)

    • 一、根元素,即HTML元素(最大的一个BFC)
    • 二、float的值不为none
    • 三、position的值为absolute或fixed
    • 四、overflow的值不为visible(默认值。内容不会被修剪,会呈如今元素框以外)
    • 五、display的值为inline-block、table-cell、table-caption
  • BFC布局规则:

    • 1.内部的Box会在垂直方向,一个接一个地放置。

    • 2.属于同一个BFC的两个相邻的Box的margin会发生重叠

    • 3.BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此, 文字环绕效果,设置float

    • 4.BFC的区域不会与float box重叠。

    • 5.计算BFC的高度,浮动元素也参与计算

  • BFC做用:

    • 1.自适应两栏布局
    • 2.能够阻止元素被浮动元素覆盖
    • 3.能够包含浮动元素---清除内部浮动 原理:触发父div的BFC属性,使下面的子div都处在父div的同一个BFC区域以内
    • 4.分属于不一样的BFC时,能够阻止margin重叠

未完待续,点击查看更多细节:第 39 题

交流

进阶系列文章汇总以下,内有优质前端资料,以为不错点个star。

github.com/yygmind/blo…

我是木易杨,公众号「高级前端进阶」做者,跟着我每周重点攻克一个前端面试重难点。接下来让我带你走进高级前端的世界,在进阶的路上,共勉!

相关文章
相关标签/搜索