Skip to content
This repository has been archived by the owner on Jan 11, 2023. It is now read-only.

2019年底面试题整理 #11

Closed
citrus327 opened this issue Oct 2, 2020 · 0 comments
Closed

2019年底面试题整理 #11

citrus327 opened this issue Oct 2, 2020 · 0 comments

Comments

@citrus327
Copy link
Owner

citrus327 commented Oct 2, 2020

2019年底面试题整理

本篇涵盖了从网上搜刮的面试题和自己平时理解不太深刻的问题,作为一种知识积累的方式。

HTML

HTML5新特性

  1. 语义化元素
  2. 一些新的属性contentediable,spellcheck等
  3. input的新类型:date, email, url等

meta标签的作用

  1. seo 优化
  2. viewreport 设置手机端适配
  3. 设置 charset 字符编码
<meta name="keywords" content="电商,物流" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta charset="utf-8" />

CSS是否影响Html解析,js解析,html渲染?

DOM解析与CSS解析是并行的,CSSOM TREE和DOM TREE完成后,会合并成为render tree。

也是为何css解析不影响html解析,但是影响html渲染。

js操作可以改变html和css样式,因为浏览器会维持顺序,所以css会阻止后面js的执行。

Reference

onload事件与DOMContentLoaded

  1. onload会等待所有的资源加载完成(html, css, js, 图片, 视频等)
  2. 如果页面中同时存在css和js,并且存在js在css后面,则DOMContentLoaded事件会在css加载完后才执行
  3. 其他情况下,DOMContentLoaded都不会等待css加载,并且DOMContentLoaded事件也不会等待图片、视频等其他资源加载。
    Reference

Worker

为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

CSS和JS的位置会影响页面效率

  1. css不会阻止html解析
  2. css会阻止html渲染(css加载完成之前,会白屏)
  3. css会阻止js运行

用一个div模拟textarea的实现

给div标签加contenteditable属性就能实现textarea的编辑功能了

图片懒加载怎么实现

判断img标签是否在可视区域,如果可视,加载图片,否则不加载。

因为src如果有值,会直接加载,所以是用一个data-src存放图片地址,等到图片在可视范围内,再把值apply到src上。

判断可视区域可以用offset自己去算,或者是用element.getBoundingClientRect去获取dom位置。

但是这些都会触发reflow, 有个API叫做IntersectionObserver,可以动态的监测dom是否可视。

repaint重绘和reflow回流

Repaint:

  • dom元素的视觉效果改变,但是不涉及任何排版布局改变
  • 主要针对单一元素的重绘
  • 例如:1. color修改,text-align,hover引起的颜色变化以及任何不改变dom结构变化的变化

Reflow:

  • dom元素的位置,结构变化
  • 因为这些变化会让浏览器重新计算dom元素所在位置和所占空间,
    这些变化大多针对整个页面,所以reflow要比repaint消耗的多
  • 例如:
    1. margin/padding
    2. 修改dom节点结构
    3. animation/transition 每一帧都会引起reflow
    4. 读取元素某些属性的时候:offsetTop/top等, getComputedStyle()
      这些属性需要依赖一些元素去计算,所以会触发reflow
    5. scroll页面, resize页面
    6. 字体大小变化

如何优化reflow

  1. 减少dom之间相互依赖,减少dom层级
  2. 少用复杂动画
  3. 减少使用css中的运算式
  4. 少改class,特别是子元素很多的
  5. 批量更新元素样式,然后设置为一个class,一下附在dom上

CSS

盒子模型

  1. 盒子具有content, padding, margin, border
  2. box-sizing:
    • 默认为content-box,width和height指定为content的高宽
    • border-box, width和height会为border+padding+content的高宽

css绘制平行四边形

父元素transform: skewX(-45deg);

子元素transform: skewX(45deg)

元素居中

  1. 行元素

    • 单行行元素,可以height = line-height或者固定上下padding
    • 多行行元素可以用flex
  2. 块元素

    • 子元素absolute定位,top:50%, transition: translateY(-50%)
    • 父元素table, 子元素table-cell, vertical-align: middle
    • flex

全居中

  1. absolute + top: 50% + left: 50% + translate(-50%, -50%)
  2. flex
  3. 父要求有高度position: absolute; top: 0; left: 0; right: 0; bottom: 0; margin: auto;

Postion values

  1. static 是默认值
  2. relative 相对定位 相对于自身原有位置进行偏移,仍处于标准文档流中
  3. absolute 绝对定位 相对于最近的已定位的祖先元素, 有已定位(指position不是static的元素)祖先元素, 以最近的祖先元素为参考标准。如果无已定位祖先元素, 以body元素为偏移参照基准, 完全脱离了标准文档流。
  4. fixed 固定定位的元素会相对于视窗来定位,这意味着即便页面滚动,它还是会停留在相同的位置。一个固定定位元素不会保留它原本在页面应有的空隙。

css 优先级

  1. 内联样式权重:1000
  2. id 选择器权重:0100
  3. 类选择器,属性选择器,伪类选择器权重:0010
  4. 元素选择器,伪元素选择器权重:0001
  5. 通配选择器 *,子选择器 >,相邻选择器 +。权重:0000

css的覆盖顺序跟css的样式声明顺序有关
css声明的越靠后,优先级越高

使用css实现一个持续的动画效果

#box {
  width: 30px;
  height: 30px;
  background-color: gray;
  animation-name: myAnimation;  /* 动画名称 */
  animation-duration: 2s; /* 动画持续时间 */
  animation-timing-function: ease-in; /* 运动函数 */
  animation-delay: 500ms; /* 动画起始延迟 */
  animation-iteration-count: infinite; /* 动画重复次数 */
  animation-direction: alternate;
  /**
  * 动画执行方向
  * normal: 每个循环内动画向前循环,换言之,每个动画循环结束,动画重置到起点重新开始,这是默认属性。
  * alternate: 动画交替反向运行,反向运行时,动画按步后退,同时,带时间功能的函数也反向
  * reverse: 反向运行动画,每周期结束动画由尾到头运行
  * alternate-reverse: 反向交替, 反向开始交替
  */
}

@keyframes myAnimation {
  20% {
    transform: translateY(20px); /* 向下20px */
  }

  50% {
    transform: translateY(40px); /* 再向下20px */
  }

  100% {
    transform: translate(40px, 20px); /* 再向右移动20px */
  }
}

使用js实现一个持续的动画效果 requestAnimationFrame

  1. 若你想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用window.requestAnimationFrame()
  2. window.requestAnimationFrame(callback)回调函数内参数是一个时间戳为Performance.now()
  3. 返回一个动画ID,可以使用window.cancelAnimationFrame()取消
const $box = document.getElementById('box')
function myAnimation(timestamp) {
  $box.style.width = `${$box.clientWidth + 1}px` // 每次调用的时候,都把宽度+1
  requestAnimationFrame(myAnimation) // 调用requestAnimationFrame去执行本函数,每次执行完之后,会继续执行动画
}
myAnimation()

左右布局:左边定宽、右边自适应,不少于3种方法

  1. float left + overflow:hidden
  2. float + margin:left
  3. float:left + (float:right, calc)
  4. flex: 外容器flex, 左边定宽,右边flex: 1撑满

BFC

/css/BFC.html
BFC是一个类似独立的隔离容器(脱离文档流),floating可以创建BFC.
float的元素会脱离文档流,如果他的父元素也是个BFC,那他们2个元素都会在BFC里,就在这个独立的容器内了。
触发条件:

  1. overflow不是visible
  2. 浮动元素
  3. 绝对定位元素
  4. 非块级元素(inline-block等)
  5. 根元素(html)或其他包含它的元素
    BFC内会触发�边距坍塌,但是2个不同的BFC就不会触发

clear: left/clear: right/clear: both
清除元素2边的浮动。例:clear:left如果施加在某个元素,那么这个元素左边的浮动元素就不是浮动元素了

JS

事件传播

事件捕获阶段,处于目标阶段,时间冒泡阶段
event.stopPropagation() 阻止事件继续传播,即取消进一步的事件捕获或冒泡

箭头函数与普通函数区别

  1. 箭头函数的this指向为声明位置的this指向,而普通function则为由他实例化的对象本身

call与apply的区别

call有N个参数,第一个参数是上下文,之后的参数就会传给目标函数,逐个列出
apply有2个参数,第一个参数是上下文,第二个参数是参数的数组

宏事件,微事件

任务队列
阮老师的Event Loop文章
Philip Roberts: Help, I’m stuck in an event-loop.
事件循环:

  1. 所有的同步事件都在主线程上运行,形成一个执行栈 (execution context stack)
  2. 如果碰到异步事件,则推入任务队列(task queue)
  3. 一旦执行栈同步任务执行完,则会读取任务队列,按照一定顺序执行任务队列里的方法。
  4. 循环

所以如果想要模拟这个场景,需要一个Call Stack, Task Queue, Microtask Queue

  1. 同步事件进入Call Stack,直接执行
  2. 如果遇到异步任务
    A. 如果是宏任务,加入Task Queue
    B. 如果是微任务,加入Microtask Queue
  3. 当前Call Stack空栈后,开始清空Microtask Queue
  4. 此时Call Stack空栈,Microtask Queue被清空,轮到UI renderer
  5. 开始执行清空Task Queue
  6. 循环

微任务 >> UI Render >> 宏任务

微任务包括 promise.resolve, promise.catch, MutationObserver

宏任务包括 setTimeout, setInterval, requestAnimationFrame

async function async1() {
  console.log('async1 start');
  await async2();
  console.log('async1 end');
}
async function async2() {
    console.log('async2');
}
console.log('script start');
setTimeout(function() {
    console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(function() {
    console.log('promise2');
});
console.log('script end');

sort

https://blog.csdn.net/k346k346/article/details/51154786

  1. 冒泡排序:双层遍历,对比前后两个节点,如果满足条件,位置互换,直到遍历结束。
  2. 快速排序:去数组中间的那一个数,然后遍历所有数,小于该数的push到一个数组,大于该数的push到另外一个数组,然后递归去排序这两个数组,最后将所有结果连接起来。
  3. 选择排序:声明一个数组,每次去输入数组里面找数组中的最大值或者最小值,取出来后push到声明的数组中,直到输入数组为空。

js Array

https://juejin.im/entry/59ae664d518825244d207196

requestAnimatedFrame

haizlin/fe-interview#1341

addeventlistener在父节点,监听子节点点击事件 => 事件委托

原型链继承,instance of, ES6 ES5的继承区别, 如何实现一个 new

https://github.com/ziyi2/js/blob/master/JS%E7%B1%BB%E5%92%8C%E7%BB%A7%E6%89%BF.md

function Person (name, age) {
  this.name = name
  this.age = age
}

Person.prototype.say = function () {
  console.log(`I'm Person, my name is ${this.name}, my age is ${this.age}`)
}
Person.prototype.yell = function () {
  console.log(`yell`)
}

function Student (name, age, score) {
  Person.apply(this, arguments)
  this.score = score
}

// 原型继承
Student.prototype = new Person()
// 修改继承来的原型上的constructor为目标Student的constructor
Student.prototype.constructor = Student

Student.prototype.say = function () {
  console.log(`I'm Student, my name is ${this.name}, my age is ${this.age}, my score is ${this.score}`)
}
Student.prototype.studentYell = function () {
  console.log(`student yell`)
}

var p = new Person('a person', '22')
var derek = new Student('derek', '21', '321')
derek.say()
derek.yell()
derek.studentYell()
console.log(derek instanceof Person)
console.log(derek instanceof Student)
// p.studentYell() 
// new 方法实现
function _new(fn, ...arg) {
  const obj = Object.create(fn.prototype);
  const ret = fn.apply(obj, arg);
  return ret instanceof Object ? ret : obj;
}

手写promise

haizlin/fe-interview#1335

节流和防抖

function debounce (fn, delay) {
  let timer = null
  return function () {
    if (timer) {
      console.log('cancel timer')
      clearTimeout(timer)
    }
    timer = setTimeout(() => {
      fn.apply(this, arguments)
    }, delay)
  }
}

function throttle (fn, delay) {
  let canRun = true
  return function () {
    if (!canRun) {
      return 
    }
    canRun = false
    fn.apply(this, arguments)
    setTimeout(() => {
      canRun = true
    }, delay)
  }
}

实现页面加载进度条

postMessage

实现拖拽功能,比如把5个兄弟节点中的最后一个节点拖拽到节点1和节点2之间

手写parseInt的实现

WeakMap Map Set WeakSet

深度优先遍历和广度优先遍历, deep clone

浏览器与HTTP

http状态码

  1. 1** 信息,服务器收到请求,需要请求者继续执行操作
  2. 2** 成功,操作被成功接收并处理 200
  3. 3** 重定向,需要进一步的操作以完成请求 304
  4. 4** 客户端错误,请求包含语法错误或无法完成请求 404
  5. 5** 服务器错误,服务器在处理请求的过程中发生了错误 500

OSI 分层

  1. 应用层:
    http协议,dhcp, ftp, pop3, smtp
  2. 表示层:
    SSL加密协议
  3. 会话层:
    会话层的作用就是为创建、管理和终止会话提供必要的方法,还负责管理和确定传输模式
    (单向 全双工 半双工)
  4. 传输层
    TCP协议 UDP协议
  5. 网络层
    IP协议:ipv4, ipv6
  6. 链路层
  7. 物理层

怎么发送一个跨域的POST请求, 同源策略?CSRF/clickjacking? CORS? JSONP? IFrame?

amandakelake/blog#62
CORS(跨域资款共享)

小提示:如果你回答跨域解决方案CORS,那么面试官一定会问你实现CORS的响应头信息Access-Control-Allow-Origin。

什么是CORS
CORS(跨域资源共享 Cross-origin resource sharing)允许浏览器向跨域服务器发出XMLHttpRequest请求,从而克服跨域问题,它需要浏览器和服务器的同时支持。

浏览器端会自动向请求头添加origin字段,表明当前请求来源。

服务器端需要设置响应头的Access-Control-Allow-Methods,Access-Control-Allow-Headers,Access-Control-Allow-Origin等字段,指定允许的方法,头部,源等信息。

请求分为简单请求和非简单请求,非简单请求会先进行一次OPTION方法进行预检,看是否允许当前跨域请求。

从输入URL到看到页面发生的全过程,越详细越好。

  1. 输入网址
  2. DNS解析
  3. 建立tcp连接
  4. 客户端发送HTPP请求
  5. 服务器处理请求 
  6. 服务器响应请求
  7. 浏览器展示HTML
  8. 浏览器发送请求获取其他在HTML中的资源。

TCP和UDP的区别

  • 主要区别在于从数据完整上,session安全上等,UDP不可靠,TCP可靠。
  • TCP协议在传送数据段的时候要给段标号;UDP协议不要
  • TCP协议可靠;UDP协议不可靠
  • TCP协议是面向连接;UDP协议采用无连接
  • TCP协议负载较高,采用虚电路;UDP采用无连接
  • TCP协议的发送方要确认接收方是否收到数据段(3次握手协议)
  • TCP协议采用窗口技术和流控制

三次握手,四次挥手

三次握手:

A: 发送了

B: 接受了,你能收到么

A: 收到了,ok,我俩连上了

四次挥手:

tcp是全双工的,就是A 正在给 B发信息的同时,B也在给A发信息

A: 我要关了

B: 可能还有一个包要传输,你稍等

B: OK, 我结束了。我这条消息你接到了,就可以关了

A: 我收到了,我关了。

Reference
Reference

浏览器的缓存机制:强缓存 协商缓存

缓存机制可能存在的3种状态,缓存命中,缓存未命中,缓存验证再命中(验证缓存新鲜度)

  1. 强缓存 200

    • 利用http头里的expires或cache-control两个字段控制
    • 这2个字段是在响应头里,也就是由浏览器本身验证
    • 当请求发出时,浏览器会判断目标资源是否命中强缓存
    • 如果命中,则直接取缓存,否则发请求
    • expires与时间戳强耦合,所以其实会出现问题。cache-control就是解决这个问题的。
    • cache-control允许传入max-age: 300000,因为传入的是时间段,所以解决了expires的时间戳耦合问题
    • Cache-Control 相对于 expires 更加准确,它的优先级也更高。当 Cache-Control 与 expires 同时出现时,我们以 Cache-Control 为准
      • cache-control的一些细节描述:
        1. no-store: 一切响应不缓存
        2. no-cache: 发送缓存之前,强制要求向原始服务器验证
        3. must-revalidate: 一旦资源过期,成功向服务器验证之前,缓存不能用该资源响应后续请求
  2. 协商缓存 304

    • 协商缓存机制下,浏览器需要向服务器去询问缓存的相关信息,进而判断是重新发起请求、下载完整的响应,还是从本地获取缓存的资源
    • 肯定会访问服务器,所以是由服务器决定是否缓存。
    • if-modified-since这个请求头会发送时间戳到服务器,服务器验证是否过期,如果没有过期,返回304
    • 如果过期了,会返回expires响应头和200状态码。下次请求if-modified-since就会以这个expires的时间戳再次请求。

优劣:

  1. 协商缓存每次都会访问服务器
  2. 强缓存如果服务器资源更新,可能会访问老接口,如果老接口下架了,就gg了

缓存的意义在于减少服务器请求,更多使用本地资源。所以要让每次文件更新之后,让缓存失效。所以可以使用hash。
html使用协商缓存,js css使用强缓存

浏览器路由实现

XSS CSRF

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

防范手段:

  1. http only cookie, 不允许js获取cookie
  2. 输入检查,数据过滤
  3. 输出检查
  4. 使用HTTP头指定内容的类型,使得输出的内容避免被作为HTML解析 application/json

CSRF,即 Cross Site Request Forgery,中译是跨站请求伪造,是一种劫持受信任用户向服务器发送非预期请求的攻击方式。

CSRF 攻击往往是在用户不知情的情况下构造了网络请求

  1. 使用验证码,强制要求用户与app交互,最终才能完成请求
  2. 使用request header, referer,
    如果访问者不是从指定路径来的,则为csrf攻击
if (req.headers.referer !== 'http://www.c.com:8002/') {
    res.write('csrf 攻击');
    return;
}
  1. csrf能成功是因为能够�截取用户的Cookie,要抵御这种攻击,可以在请求中带入一个参数,后端进行校验。
    例如, jwt

cookie token 优劣

  1. 首先token不是防止XSS的,而是为了防止CSRF的;
  2. CSRF攻击的原因是浏览器会自动带上cookie,而浏览器不会自动带上token

Reference

介绍 HTTPS 握手过程

A: 发送了

B: 接受了,请发送clientKey

A: 给你我的KEY

B: ok,收到了

Reference

websocket

  1. 基于TCP的一个全双工通信协议,HTML5的新特性
  2. onclose, onerror, onmessage, onopen
  3. socket.close(); socket.send()
  4. 兼容性ie >= 10

Vue

Computed 缓存实现和依赖收集实现

keep alive实现

vdom实现,differ算法,key的作用

  • 简单描述一下Vue.js的render过程

    1. Vue文件会解析成3个片段,HTML会parse成AST
    2. 解析成vdom
    3. 映射成真实dom
    4. Mount
    5. 如果数据变动,触发Object.defineProperty的setter
    6. differ原始vdom和新的vdom
    7. 找到当中的区别, 变更differ出来的dom
  • vdom:

    1. vdom其实就是代表真实dom的轻量js对象,他仅仅记录了部分需要的内容
    2. 在操作dom的时候,可以先用vdom去计算到底有多少改动
    3. 在一次dom操作中,直接进行变动,这样可以减少dom操作
    4. 操作vdom并不是比真实dom操作快,毕竟如果已知dom的区别,直接操作dom肯定比操作vdom再映射区别的方式好,实际的原因是vdom其实只是充当一个中间层,能够帮助寻找新旧dom的区别,具有普适性,可以在可维护性和效率上达到一个平衡。
    5. 目前Vue.js使用的vdom lib是 snabbdom

总结一下就是为了找到dom变化而衍生出来的一种方式。

  • differ算法 Reference:
    1. 形成新旧2个vnode tree
    2. 从根节点往下进行differ, 同层比较。
    3. 如果发现根节点不同,直接替换。否则继续之前的操作,直到整棵树遍历完全。
    4. patchVnode检查节点,patchVnode中调用updateChildren去differ子节点
    5. updateChildren:
      • updateChildren是通过设定头尾2个指针,向中间遍历,也就是oldStartIndex, oldEndIndex, newStartIndex, newEndIndex
      • 循环条件是startIndex <= endIndex, 也就是2个指针汇聚,则终止遍历
        differ

Reference

完成一个Dialog组件,说 说你设计的思路

为什么 Vuex 的 mutation中不能做异步操作

官方约定action中可以发送异步请求,而mutation不可以。是因为方便开发debug/devtools去检测数据的走向。

Object.defineProperty 和 Proxy

  1. Object.defineProperty
  • Object.defineProperty对数组无法做到检测
  • 必须遍历每一个key
  • 如果object的层级深,需要遍历并对更深层的子对象进行监测
  • Vue在监测数组的时候,其实是重写了Array的那些原型方法,达到劫持的目的
  1. Proxy
  • 针对整个对象的监测,所以不需要遍历
  • 支持Array的监测
  • Proxy也需要遍历子元素进行监测

Vue 的父组件和子组件生命周期钩子执行顺序是什么

Advanced-Frontend/Daily-Interview-Question#128

$nextTick

https://juejin.im/post/5cd9854b5188252035420a13
DDFE/DDFE-blog#24

Webpack

https://segmentfault.com/a/1190000016816813

性能优化方案

  1. webpack分chunk, 使用splitChunk,将vendor.js和bundle.js分开
  2. vendor.js可以再根据node_module name再分,利于缓存
  3. 压缩代码
  4. 局部引入代码,例如不完整引入lodash
  5. 静态代码可以通过字蛛进行字体压缩

references

  1. https://juejin.im/post/5befeb5051882511a8527dbe
  2. https://juejin.im/post/5bf8fe2ee51d452d705fee3d
  3. https://juejin.im/post/5ab0da85f265da23866fb9b7
  4. https://muyiy.vip/question/
  5. https://juejin.im/post/5d23e750f265da1b855c7bbe

Questions:

1.改造下面的代码,使之输出0 - 9,写出你能想到的所有解法。

for (var i = 0; i< 10; i++){
  setTimeout(() => {
    console.log(i);
    }, 1000)
}
  1. 下面代码输出什么
var a = 10;
(function () {
  console.log(a)
  a = 5
  console.log(window.a)
  var a = 20;
  console.log(a)
})()
@citrus327 citrus327 changed the title 2019年底面试题整体 2019年底面试题整理 Oct 2, 2020
@citrus327 citrus327 reopened this Oct 2, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

1 participant