前端常见面试问题(Keep updating)

Browser

浏览器引擎

  1. 渲染引擎:获取解析网页内容,构建DOM、CSS等,计算网页layout,绘制网页
  2. JavaScript引擎:解释执行JavaScript

随着JavaScript的发展,分离出了JavaScript引擎。

浏览器内核

  • Trident: IE系,包括360,搜狗
  • Gecko: Firefox系
  • WebKit: Safari系,以及魔改版Webkit的Chrome

HTML

HTML5新特性

  • 语义化标签
  • 音频视频API
  • Canvas API
  • localStorage 与 sessionStorage
  • WebSocket

语义化

在HTML5中,所有标签具有其书面上的语义,即具有他要传递的内容是什么。语义与显示分离,显示全部由CSS3负责。

优点:

  1. 提升可访问性(对于使用ScreenReader)与互操作性
  2. 改进搜索引擎优化
  3. 一般使HTML文件更小
  4. 更好维护,表示层在CSS中

语义标签

  • <nav>导航标签,里面通常是列表<ul>或者<ol>
  • <article>表示一个页面、文档、应用、容器,对<section>的聚合
  • <section>表示文章中的一个段落、一个对话框的标签页等
  • <aside>侧栏标签,包括侧边栏、广告、友情链接等
  • <label for="xx">定义form关系,点击label会跳转到相关的input

Cookie与localStorage、sessionStorage区别 (多标签页之间通信)

cookie小于4kb,在每次http请求中携带,具有设定的有效期

localStorage和sessionStorage大于4MB,永久存储/关闭窗口后消失,保存在本地

离线存储网页

在没有因特网时通过缓存使用网页,在有网络连接时更新网页缓存

用法:

1
2
3
<html manifest = "cache.manifest">
...
</html>

cache.mainfest文件内书写缓存规则:

  1. CACHE:表示需要离线存储的资源列表
  2. NETWORK:表示在它下面列出来的资源只有在在线的情况下才能访问,他们不会被离线存储
  3. FALLBACK: 如果第一个资源访问失败,就访问第二个资源替代

CSS

盒模型

content + padding + border + margin

box-sizing属性: content-box(默认值) or border-box,前者width指定content的宽度,后者width指定了content+padding+border的宽度

块级元素与行内元素

display: block块级元素具有自定义的width、height属性,并且左右换行

display: inline行内元素不具有width等属性,由内容撑开,左右不换行

display: inline-block左右不换行的行内显示,但是可以指定width和height等属性。

Float与清除浮动

浮动元素脱离文档流。浮动的框可以向左或向右移动,直到他的外边缘碰到包含框或另一个浮动框的边框为止。

可以实现文字环绕图片,但是浮动可能导致的问题。Image的父容器div依靠内部元素撑开,如果图片浮动,父div:

  1. 父元素height无法被撑开
  2. 背景不显示(因为没有height)
  3. margin值不正确显示

解决方法:

  1. 给父元素添加属性overflow: auto
  2. 加入一个兄弟节点,具有属性clear: both
  3. 将上面方法用CSS after实现

文档流(normal flow)与定位(Position)

文档流是相对于盒模型来讲的,指从左到右从上到下的正常布局

float: leftposition: fixedposition: absolute会导致DOM脱离文档流,位于上一层

  1. static:忽略top、left、z-index等声明,出现在正常文档流
  2. relative:相对于其正常位置
  3. absolute:相对于非static的最近祖先
  4. fixed:相对于浏览器窗口

可以通过z-index来改变文档堆叠顺序,其计算顺序为: 子元素的z-index不能大于其父元素,越大越位于上方。

CSS选择器和权重

以 10 为基本单位
1000.内敛选择器 > 100.ID选择器 > 10.class选择器 > 1.元素选择器

同优先级最后一个覆盖前一个

垂直/水平居中的实现方法

方法一: 利用绝对定位

1
2
3
4
5
6
.vertical {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}

方法二: 利用table的效果(父级container高度自适应)

1
2
3
4
5
6
7
8
#wrapper {
display: table;
}
#cell {
display: table-cell;
vertical-align: middle;
}

方法三: flex布局

通过align-self: center属性实现交叉轴(竖直方向)的居中

单行文字垂直居中:

设置line-heightheight相等

多行文字垂直居中:

  1. 设置padding-top等于padding-bottom
  2. 设置一个span包围多行文字,设置spandiplay:block,然后可以设置vertical-align

JavaScript

延迟加载JS的方法

  • defer 和 async
  • 动态创建DOM,创建script标签,设置src属性并插入DOM tree(JSONP的实现)

原生JS操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//创建与添加
createElement(); //创建一个具体的元素
createTextNode(); //创建一个文本节点
parentNode.appendChild();
parentNode.insertBefore(newNode, referenceNode);
//删除单个节点
parent.removeChild(child);
node.parentNode.removeChild(node);
//替换DOM节点
parentNode.replaceChild(newChild, oldChild);
//节点移动属性
element.parentNode; element.nextSibling; element.previousSibling;
element.childNodes; //返回数组
element.firstChild; element.lastChild;
//查找DOM节点
document.getElementById();
document.getElementsByClassName();
document.getElementsByTagName();
document.querySelectorAll(); //具有更广泛的选择性(接受CSS Selector),IE8兼容

Web优化策略

  1. 请求数量:合并脚本和样式表, iconfont,拆分初始化负载(一开始只加载必要脚本),划分主域(增加DNS查询代价,但是增加了并发链接数)
  2. 请求带宽:开启 GZip,精简 JavaScript,移除重复脚本,图像优化
  3. 利用缓存:使用 CDN,使用外部 JavaScript 和 CSS,减少 DNS 查找
  4. 页面结构:将样式表(影响样式的内容)放在顶部,将脚本放在底部,尽早刷新文档的输出

Web应用从服务器主动推送Data到客户端

  1. HTML5提供的WebSocket
  2. AJAX 长链接
  3. AJAX 长轮询

一个页面从输入URL到加载完成的过程

  1. 键盘按下发出中断,扫描其接口信息,经过操作系统传递给应用(浏览器);
  2. 浏览器开启一个线程来处理这个请求,对 URL 分析判断如果是 http 协议就按照 Web 方式来处理;
  3. 通过DNS解析获取网址的IP地址,设置 UA 等信息发出第二个GET请求;
  4. 进入HTTP会话;
  5. 服务器端Apache、Node.JS等响应请求,根据路由,经过相应应用(中间件)处理;
  6. 结束处理,如果修改时间和缓存一致返回304,否则返回200和该资源;
  7. HTML一边下载一边解析,根据标签建立文档树 DOM;
  8. 根据标记下载所需CSS、JS、图片文件,CSS阻塞式简历CSSOM,最终合并DOM和CSSOM,形成render tree,进行layout和painting;
  9. 当 JS 运行完成,页面加载完成。

new操作符做了什么

  1. 新建一个空白对象,并且继承该函数的原型,然后用this引用该对象
  2. 添加属性和方法
  3. 隐式返回this

JavaScript继承

1
2
SubClass.prototype = Object.create(SuperClass.prototype); //子类原型继承自父类
SubClass.prototype.constructor = SubClass; //修改constructor

闭包

闭包是一个嵌套在内层的函数和被他所捕获的外层变量

1
2
1. 闭包是指函数有自由独立的变量。也就是,定义在闭包中的函数可以“记忆”它创建时候的环境(即其作用域存在的所有变量)。
2. 闭包是一种特殊的对象。它由两部分构成:函数,以及创建该函数的环境(两者合起来可以视作为整个外层函数 makeFunc 的返回值 myFunc)

跨域及其解决方案 (腾讯)

详情参见同源政策与跨域详解

  1. CORS: 需要浏览器+服务器的支持。浏览器在Header中加入Origin字段,如果Origin在许可范围,服务器返回响应多出几个Access-Control字段

// Todo:CORS详解,同服务器不同域名请求 (腾讯)

  1. JSONP:
    1
    2
    3
    JSONP在URL中向服务器传递一个callback function,在JavaScript中定义了该function的行为。
    当服务器端接收到请求后,将数据用该function包括起来,类似于 jsonp(data) { ... }并返回该脚本文件。
    客户端接收到请求后,自动运行之前定义个callback function,即对跨域数据进行了操作

AJAX操作

  1. 创建XMLHttpRequest对象 (状态0)
  2. 设置HTTP请求状态变为4(完成)时的回调函数
  3. 通过OPEN创建请求,指定方法,URL等 (状态1)
  4. SEND发送请求 (状态2)
  5. 使用JavaScript实现DOM刷新 (状态3下载,4时调用回调函数)

利用setTimeout多次调用

在其回调函数再次调用自身即可

事件冒泡与事件捕获

  • 事件冒泡:从子元素到父元素依次触发其上事件的回调函数
  • 事件捕获:先触发父元素上的事件,再向下依次传递到子元素

addEventListener中,第三个参数设置为true启用事件捕获。当既存在事件冒泡,又存在事件捕获时,优先事件捕获

事件代理/事件委托 (jQuery 中 bind 和 on)的区别

事件委托将需要多个子元素触发的事件绑定到父元素上,通过 e.target 来判断是否是指定的子元素,进而触发回调

Attribute 和 Property 的区别

前者是DOM作为HTML标签的属性,后者是DOM作为JavaScript对象的属性。

  • 共同点:许多attribute具有对应的property
  • 不同点:用户自定义的attribute和自定义的property之间没有关系

Promise 规范

解决了之前异步操作的Callback Hell,将横向的操作变为了纵向的操作。具有4种状态:pending、fulfilled、rejected、settled。

使用:将之前的异步回调包装进Promise,操作成功调用resolve、失败调用reject。然后在then中运行原本的回调任务。

改变运行上下文

方法:bindapplycall

用处:绑定上下文(保持其始终获取某个上下文)、代理函数(自定义log代理console.lo)等

如何让接受多个参数的函数接受一个数组作为参数(例如 Math.max) (讯飞)

1
2
3
4
//方法1
Math.max.apply(Math, [1,2,3,4,5]);
//方法2 ES6
Math.max(...[1,2,3,4,5]);

DOM操作性能

  • 使用变量缓存DOM查询结果,减少查询操作
  • 减少导致页面重绘(layout painting)的操作(改变widht、height。使用CSS动画)
  • 使用更加精准的选择器(id选择器、类选择器)

前端渲染与后端渲染比较

前端渲染不利于SEO,服务器端为了前端渲染,需要花费更多时间将对象字符串化

后端渲染增大了网络传输体积与损耗,容易造成延时(白屏)

Virtual DOM

DOM元素非常庞大,具有复杂的属性,牵一发而动全身。通过JavaScript来模拟DOM树的结构,加快响应时间

  1. 建立虚拟DOM树:用JavaScript对象记录节点类型、属性、子节点。通过递归该虚拟DOM建立真正的DOM树
  2. diff算法:因为跨层DOM操作很少,只比较同层DOM,从而将O(n^3)的比较降低到O(n)复杂度,深度优先遍历,记录差异:

    • 替换节点类型: div -> p
    • 增、删、调换子节点
    • 修改节点属性
    • 修改文本内容

    记录差异类型,差异内容,压入patch数组

  3. 根据patch数组,对真实DOM进行操作。

Babel 运行时 (腾讯)

Babel可以编译几乎所有ES6语法(比如块级作用域,class语法糖),但是对于环境不支持的API没有办法,解决方案主要是:

  1. babel-polyfill: 在全局对象global上挂载对象API,比如Promise等,缺点是会污染模块使用者
  2. babel-runtime: 手动在模块中require需要的API,比如const Promise = require('babel-runtime/core-js/promise'),缺点是打包可能存在多个引入语句,通过babel-plugin-transform-runtime模块重新打包避免这个问题

JSON概念,作用,结构

JSON是一种数据格式,可以用来交换、存储数据。从JSON可以方便的生成JS对象。

其语法可以认为是JS Object的子集,主要区别在:

  1. JSON的键必须带引号,JS可以不带(解释器自动加)
  2. JSON没有函数、undefined、NaN等数据类型

网络

TCP 协议的三次握手

  1. Client -> Server : SYN = 1, SEQ = x
  2. Server -> Client : SYN = 1, ACK = x+1, SEQ = y
  3. Client -> Server : ACK = y+1, 可以携带payload

HTTP状态码

  1. 1**:暂时性回复。表示接收到请求并且继续处理
  2. 2**:响应成功。表示动作被成功接收、理解和接受
  3. 3**:重定向。
    • 301:请求的网页已永久移动
    • 302:临时重定向
    • 304:自从上次请求,内容未更改过
  4. 4**:错误请求类
    • 403:禁止访问
    • 404:找不到如何与 URI 相匹配的资源
  5. 5**:服务器内部错误

WebSocket

在HTTP中,链接必须严格遵守1 request 1 response的规范,并具有HTTP header开销

WebSocket是一种兼容现有浏览器握手规范的新协议。用于实现低延迟的长链接,双方都可以随意发送数据。解决了以往需要用AJAX长轮询实现的推送,并且为浏览器FPS等低延迟需求带来可能。

缺点目前主要是浏览器兼容性,服务器维持长链接也需要一定成本。

安全

TLS(SSL) 原理详解 (腾讯)

HTTPS可能被监听/篡改吗?
// Todo….

SQL注入

通过把SQL命令插入到Web表单中递交,或插入到输入包含查询字符串(query string)的 url,最终达到欺骗服务器执行恶意的SQL命令

前端需要做到:

  1. 永远不信任用户输入,在后端对用户输入校验
  2. 加密密码等关键信息

CSRF

在危险网站B,向信任网站A发出请求(如转账),因为携带了A的Cookie,所以A认为是你自己发出的

前端需要做到:使用Cookie中不保存的,不可伪造的数据

  1. 添加验证码
  2. 添加token并且验证

XSS

攻击者向网站插入恶意HTML、JavaScript代码(比如留言板,论坛发帖),其他人看到时,将运行这段恶意代码

前端需要做到:

  1. 对用户输入的<'等特殊字符加以过滤
  2. 转义特殊字符到HTML