theme: juejin
highlight: vs2015
前言
原文来自 我的个人博客
最近一直在学习 vue3
源码,搞的头有点大,用这篇文章来换换脑子~
PS:面试题下附有解答,解答会结合我的思考以及扩展,仅供参考,如果有误麻烦指出,好了就酱~
1. HTML
在面试中,HTML
的面试题相对来说比较简单,一般面试官不会花太多时间去关注 HTML
的细节。
1.1 如何理解 HTML 语义化
语义化的含义就是用正确的标签做正确的事情,html
语义化就是让页面的内容结构化。
打个比方就是,如果我要实现一个一级标题,可以用 div+css
设置样式字体来达到效果,也可以用 h1
,前者当然也能实现效果,但是语义化的方式还是使用 h1
标签,因为我们一看到 h1
标签就会知道他是一个 一级标题,这就是 html
语义化。
- 语义化不仅能方便我们开发人员阅读维护理解
- 还有利于搜索引擎的建立索引、抓取(
SEO 优化
) - 而且有还利于不同设备的解析(屏幕阅读器,盲人阅读器等)
1.2 谈谈 SEO
这个问题可以从三个方向回答:
SEO
是什么?- 它的原理是?
SEO
优化方法有哪些?
回答:
SEO(Search Engine Optimization)
,意思就是搜索引擎优化。通俗点讲就是提高你的网页在搜索结果中的排名(排名越高越靠前)SEO
的原理可以大致分为四个步骤:- 爬行和抓取:搜索引擎派出称为爬虫的程序,从一只网页出发,就像正常用户的浏览器一样访问这些网页抓取文件,并且会跟踪网页上的链接,访问更多的网页。
- 索引:搜索引擎将爬取的网页文件分解、分析存入数据库,这个过程就是索引。
- 搜索词处理:用户输入关键词,点击搜索后,搜索引擎会进行
分词
检查拼音
错别字
等等。。。 - 排序:搜索引擎从索引数据库中找出所有包含搜索词的网页,并按照计算法进行排序返回。
SEO
优化可以分为内部优化和外部优化内部优化
- 标签优化:语义化标签、META 标签
- 网站内容更新:每天保持站内的更新(主要是文章的更新等)
- 服务器端渲染(
SSR
) - 内部链接的优化,包括相关性链接(
Tag
标签),锚文本链接,各导航链接,及图片链接
外部优化:
- 外部链接类别:博客、论坛、
B2B
、新闻、分类信息、贴吧、知道、百科、相关信息网等尽量保持链接的多样性 - 外链运营:每天添加一定数量的外部链接,使关键词排名稳定提升。
- 外链选择:与一些和你网站相关性比较高,整体质量比较好的网站交换友情链接,巩固稳定关键词排名
- 外部链接类别:博客、论坛、
1.3 HTML 有哪些块级元素,有哪些行内元素以及他们的区别
display: block/inline/inline-block
- 常用的块级元素:div、p、h1-6、table、form、ul、dl
- 常用的行内元素:a、span、br、label、strong
- 常用的内联块状元素有:img、input
- 默认情况下块级元素会独占一行而行内元素不会
- 块级元素可以设置
width
,height
属性而行内元素设置无效 - 块级元素可以设置
margin
和padding
,而行内元素只有水平方向有效,竖直方向无效 - 块级元素可以包含行内元素和块级元素。行内元素不能包含块级元素
1.4 什么是 HTML5,和 HTML 的区别是?
HTML5
是 HTML
的新标准,其主要目标是无需任何额外的插件如 Flash
、Silverlight
等,就可以传输所有内容。它囊括了动画、视频、丰富的图形用户界面等。
区别:
- 从文档声明类型上看:
HTML
是很长的一段代码,很难记住。HTML5
却只有简简单单的声明,方便记忆。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<!DOCTYPE html>
- 从语义结构上看:
HTML4.0
没有体现结构语义化的标签,通常都是这样来命名的<div id="header"></div>
,这样表示网站的头部。HTML5
在语义上却有很大的优势。提供了一些新的标签,比如:<header><article><footer>
。
1.5 HTML5 有哪些新特性?
- 新增语义化标签:
nav
、header
、footer
、aside
、section
、article
- 音频、视频标签:
audio
、video
- 数据存储:
localStorage
、sessionStorage
canvas
(画布)、Geolocation
(地理定位)、websocket
(通信协议)input
标签新增属性:placeholder
、autocomplete
、autofocus
、required
history API
:go
、forward
、back
、pushstate
2. CSS
不多说了,前端面试 CSS
是必考知识,不过关直接回家
2.1 谈谈你对盒模型的理解
思路:
- 先讲盒模型是什么
- 再介绍两种盒模型的区别
- 可以再提一下
box-sizing
这个属性
回答:
当对一个文档进行布局(
layout
)的时候,浏览器的渲染引擎会根据标准之一的 CSS基础框盒模型(CSS basic box model),将所有元素表示为一个个矩形的盒子(box
)。通俗来讲就是网页是由一个一个盒子组成的,而这个盒子是有自己的标准的,一个盒子由以下四个部分组成:content(盒子的内容)
padding(盒子的内边距)
border(盒子的边框)
margin(盒子的外边距)
在
CSS
中,盒子模型可以分成:标准盒子模型 和 怪异盒子模型- 标准盒子模型:浏览器默认的盒子模型
盒子总宽度 =
width
+padding
+border
+margin
- 标准盒子模型:浏览器默认的盒子模型
盒子总高度 = height
+ padding
+ border
+ margin
**`width/height` 只是内容高度,不包含 `padding` 和 `border` 值**
- **怪异盒子模型**
盒子总宽度 = `width` + `margin`
盒子总高度 = height
+ margin
**`width/height` 包含了 `padding` 和 `border` 值**
CSS
中的box-sizing
属性定义了引擎应该如何计算一个元素的总宽度和总高度
content-box
默认值,元素的width/height
不包含padding
,border
,与 标准盒子模型表现一致border-box
元素的width/height
包含padding
,border
,与怪异盒子模型表现一致inherit
指定box-sizing
属性的值,应该从父元素继承
2.2 margin 的纵向重叠问题
下面我们来看一段代码:
<body>
<div style="margin-top: 20px">
<div style="margin-top: 10px">aaa</div>
</div>
<p style="margin-bottom: 50px">bbb</p>
<p style="margin-top: 10px">ccc</p>
<p style="margin-bottom: 50px">ddd</p>
<p></p>
<p></p>
<p></p>
<p style="margin-top: 10px">eee</p>
</body>
请问:
aaa
距离最顶部多少 px?bbb
距离ccc
多少 px?ddd
距离eee
多少 px?
公布答案: 分别是 20px
50px
50px
常见的重叠现象
- 同一层相邻元素之间,相邻元素外边距重叠
- 父元素与子元素重叠
- 空的块级元素重叠
解决方法:
相邻元素之间:
- 底部元素设置为浮动
float:left
; - 底部元素的
position
的值为absolute/fixed
- 在设置
margin-top/bottom
值时统一设置上或下(推荐)
- 底部元素设置为浮动
父子元素之间:
- 外层元素添加
padding
- 外层元素
overflow:hidden/auto
(推荐); - 外层元素透明边框
border:1px solid transparent
; - 内层元素绝对定位
postion:absolute
: - 内层元素加
float:left
;或display:inline-block
;
- 外层元素添加
2.3 margin-left/right/bottom/top 分别设为负值会怎么样?
margin-top
和margin-left
负值,元素向上、向左移动margin-right
负值,右侧元素左移,自身不受影响margin-bottom
负值,下方元素上移,自身不受影响
第一点应该很好理解,下面这张图分别展示了第二和第三点。
2.4 BFC 的理解
1. 定义:
BFC
全称:Block Formatting Context
, 名为 "块级格式化上下文"。(全程说出来也能加分)
W3C
官方解释为:BFC
它决定了元素如何对其内容进行定位,以及与其它元素的关系和相互作用,当涉及到可视化布局时,Block Formatting Context
提供了一个环境,HTML
在这个环境中按照一定的规则进行布局。
简单来说就是,BFC
就是一块独立渲染区域,内部元素的渲染不会影响边界以外的元素。BFC
有自己的规则和触发条件。
2. 规则
BFC
就是一个块级元素,块级元素会在垂直方向一个接一个的排列BFC
就是页面中的一个隔离的独立容器,容器里的标签不会影响到外部标签- 垂直方向的距离由
margin
决定, 属于同一个BFC
的两个相邻的标签外边距会发生重叠 - 计算
BFC
的高度时,浮动元素也参与计算
3. 怎样触发 BFC
overflow: hidden
display: flex / inline-block / table-cell
position: absolute / fixed
4. BFC 解决了什么问题
1. 使用 float
脱离文档流,造成高度塌陷
<style>
.box {
margin: 100px;
width: 100px;
height: 100px;
background: red;
float: left;
}
.container {
border: 1px solid #000;
}
</style>
<div class="container">
<div class="box"></div>
<div class="box"></div>
</div>
效果:
给 container
触发 BFC
就可解决问题,例如 给 container
元素 添加 display:inline-block
。效果:
2. margin
边距重叠问题
这个问题 2.2
已经讲过:
<style>
.box {
margin: 10px;
width: 100px;
height: 100px;
background: #000;
}
.container {
margin-top: 20px;
overflow: hidden;
}
</style>
<div class="container">
<div class="box"></div>
</div>
效果以及触发 BFC
后的效果如下:
2.5 css 如何画三角形
原理:css
画三角形的原理就是通过 border
画的,因为在 css
中 border
并不是一个矩形,而是一个梯形,在 box
的内容越来越小时,border
的一条底边也会越来越小,直到 box
的宽高都是 0
时,此时的四条 border
就组成了四个三角形,将其他三个隐藏,就能得到一个三角形。
<style>
.box {
/* 内部大小 */
width: 0px;
height: 0px;
/* 边框大小 只设置两条边*/
border-top: #4285f4 solid;
border-right: transparent solid;
border-width: 85px;
/* 其他设置 */
margin: 50px;
}
</style>
<div class="box"></div>
效果:
2.6 absolute 和 relative 分别依据什么定位?
relative
依据自身定位absolute
依据最近一层的定位元素
2.8 居中对齐的实现方式
居中的方式有很多,我最常用的还是 flexbox
:
display:flex;
justify-content: center; // 水平居中
align-items: center; // 垂直居中
另外常见的还有:
- text-align: center
- line-height 和 height 设置成一样的高度
- margin: auto 0
- 绝对定位 + margin 负值
- 绝对定位 + transform 负值
2.9 line-height 的继承问题
请问下面代码 p
标签的行高是多少
<style>
body {
font-size: 20px;
line-height: 200%;
}
p {
font-size: 16px;
}
</style>
<p>AAA</p>
因为上面的代码中 p
标签没有自己的行高,所以会继承 body
的行高,这题考的就是行高是如何继承的。
规则:
- 写具体数值,如
30px
, 则直接继承该值 - 写比例,如
2 / 1.5
,则继承该比例 - 写百分比,如
200%
, 则继承计算出来的值
答案:40px
2.10 rem 是什么?
rem
是一个长度单位:
px
,绝对长度单位,最常用em
,相对长度单位,相对于父元素,不常用rem
,相对长度单位,相对于html
根元素,常用于响应式布局
2.11 CSS3 新增了哪些特性?
1. css3 中新增了一些选择器,主要为下图:
2. css3 中新增了一些样式
- 边框:
border-radius
box-shadow
border-image
- 背景:
background-clip
、background-origin
、background-size
和background-break
- 文字:
word-wrap
text-overflow
text-shadow
text-decoration
- 颜色:
rgba
与hsla
- 动画相关:
transition
transform
animation
- 渐变:
linear-gradient
radial-gradient
- 其他:
flex布局
grid布局
(上面提及的属性都应该去了解!)
3. JS 基础-变量类型和计算
不会变量,别说会 JS
3.1 JavaScript 中的数据类型有哪些?
JS 中的数据类型分为 值类型(基本类型) 和 引用类型(对象类型)
- 基本类型有:
undefined
、null
、boolean
、string
、number
、symbol
、BigInt
七种 - 引用类型有:
Object
、Function
、Array
、RegExp
、Date
等等
3.2 null 和 undefined 有什么区别?
首先 undefined
和 null
都是基本数据类型。
undefined
翻译过来是 未定义。表示 此处应该有一个值,但是还没有定义null
翻译过来是 空的。表示 没有对象,即此处不应该有值
典型用法:
- 一般变量声明了但还没有定义的时候会返回
undefined
,函数中没有返回值时默认返回undefined
,以及函数参数没提供时这个参数也是undefined
null
作为对象原型链的终点。null
作为函数的参数,表示该函数的参数不是对象。
typeof undefined
值为 undefined
,typeof null
是 'object'
undefined == null // true
undefined === null // false
至于为什么在 js
中会有 null
和 undefined
吗,就要从历史谈起了,建议阅读一下阮一峰老师的文章 undefined与null的区别
我的总结就是,JS
最初是只设计了一种表示 “无” 的值的就是 null
,这点很好理解就像 java
以及其他语言中的 null
一样,但是在 JS
中的数据类型分为 基本类型 和 对象类型,JS
的作者认为表示 “无” 的变量最好不是一个对象,另外 JS
早期没有错误处理机制,有时null
自动转为 0
,很不容易发现错误,而 undefined
会转换成 NaN
。
3.3 谈谈 Javascript 中的类型转换机制
JS
的类型转换分为:显式转换
和 隐式转换
1. 显式转换
显式转换常见的方法有:
Number
:将任意类型的值转化为数值
Number(324) // 324
// 字符串:如果可以被解析为数值,则转换为相应的数值
Number('324') // 324
// 字符串:如果不可以被解析为数值,返回 NaN
Number('324abc') // NaN
// 空字符串转为0
Number('') // 0
// 布尔值:true 转成 1,false 转成 0
Number(true) // 1
Number(false) // 0
// undefined:转成 NaN
Number(undefined) // NaN
// null:转成0
Number(null) // 0
// 对象:通常转换成NaN(除了只包含单个数值的数组)
Number({a: 1}) // NaN
Number([1, 2, 3]) // NaN
Number([5]) // 5
parseInt
:和Number
相比,Number
会更严格一些,只要有一个字符无法转换成数值,整个字符串就会被转为NaN
,而parseInt
函数逐个解析字符,遇到不能转换的字符就停下来例如:
Number('32a3') // NaN
parseInt('32a3') // 32
String
:可以将任意类型的值转化成字符串
// 数值:转为相应的字符串
String(1) // "1"
//字符串:转换后还是原来的值
String("a") // "a"
//布尔值:true转为字符串"true",false转为字符串"false"
String(true) // "true"
//undefined:转为字符串"undefined"
String(undefined) // "undefined"
//null:转为字符串"null"
String(null) // "null"
//对象
String({a: 1}) // "[object Object]"
String([1, 2, 3]) // "1,2,3"
Boolean
:可以将任意类型的值转为布尔值
Boolean(undefined) // false
Boolean(null) // false
Boolean(0) // false
Boolean(NaN) // false
Boolean('') // false
Boolean({}) // true
Boolean([]) // true
Boolean(new Boolean(false)) // true
- 隐式转换
在 JS 中,很多时候会发生隐式转换,我们可以归纳为两种场景:
- 比较运算(
==
、!=
、>
、<
)、if
、while
需要布尔值的地方 - 算术运算(
+
、-
、*
、/
、%
)
上面的场景有个前提就是运算符两边的操作数要是不同一类型的
- 自动转换为布尔值
在需要布尔值的地方,就会将非布尔值的参数自动转为布尔值,系统内部会调用Boolean函数。
undefined
null
false
+0
-0
NaN
""
这些都会被转化成 false
,其他都换被转化成 true
自动转换成字符串
遇到预期为字符串的地方,就会将非字符串的值自动转为字符串
常发生在 +
运算中,一旦存在字符串,则会进行字符串拼接操作
'5' + 1 // '51'
'5' + true // "5true"
'5' + false // "5false"
'5' + {} // "5[object Object]"
'5' + [] // "5"
'5' + function (){} // "5function (){}"
'5' + undefined // "5undefined"
'5' + null // "5null"
自动转换成数值
除了 +
有可能把运算子转为字符串,其他运算符都会把运算子自动转成数值
'5' - '2' // 3
'5' * '2' // 10
true - 1 // 0
false - 1 // -1
'1' - 1 // 0
'5' * [] // 0
false / '5' // 0
'abc' - 1 // NaN
null + 1 // 1
undefined + 1 // NaN
3.4 == 和 ===有什么区别,分别在什么情况使用?
==
叫 等于操作符===
叫 全等操作符
==
在比较时会进行隐式的类型转换
'' == '0' // false
0 == '' // true
0 == '0' // true
false == 'false' // false
false == '0' // true
false == undefined // false
false == null // false
null == undefined // true
' \t\r\n' == 0 // true
比较 null
的情况的时候,我们一般使用相等操作符 ==
const obj = {};
if(obj.x == null){
console.log("1"); //执行
}
等同于下面写法
if(obj.x === null || obj.x === undefined) {
...
}
使用相等操作符 (==
) 的写法明显更加简洁了
所以,除了在比较对象属性为 null
或者 undefined
的情况下,我们可以使用相等操作符 (==
),其他情况建议一律使用全等操作符 (===
)
3.5 let、var、const 的区别
let
和 const
都是 ES6
新增加了两个重要的关键字,var
是 ES6
之前就有的,
他们都是用来声明变量的,const
是用来声明一个只读的常量。
var
、let
、const
三者区别可以围绕下面五点展开:
- 变量提升:
var
可以在声明之前调用,let
和const
不行(会报错) - 块级作用域:
var
不存在跨级作用于,let
和const
有 - 重复声明:
var
允许重复声明变量,let
和const
不行 - 修改声明的变量:
var
和let
可以,const
不行 - 使用:能用
const
的情况尽量使用const
,其他情况下大多数使用let
,避免使用var
3.6 const 声明了数组,还能 push 元素吗,为什么?
可以
数组是引用类型,const
声明的引用类型变量,不可以变的是变量引用始终指向某个对象,不能指向其他对象,但是所指向的某个对象本身是可以变的
3.7 0.1 + 0.2 == 0.3?
答案是 false
不相等。
原因:0.1
和 0.2
在转换成二进制后会无限循环,由于标准位数的限制后面多余的位数会被截掉,此时就已经出现了精度的损失,相加后因浮点数小数位的限制而截断的二进制数字在转换为十进制就会变成 0.30000000000000004
。
总结一句话就是:二进制模拟十进制进行计算时的精度问题
3.8 值类型 和 引用类型 的值得计算
1. 第一道
var a = {"x": 1};
var b = a;
a.x = 2;
console.log(b.x);
a = {"x": 3};
console.log(b.x);
a.x = 4;
console.log(b.x);
答案放在了下面,思考一下哦~
原理:
- 首先:第一个
log
打印2
大家应该都没问题
- 然后
a
指向了栈内存中的另一块地址,而b
没变,所以b.x
仍然为2
- 接着修改
a.x = 4
,因为此时b
仍然指向之前的地址,所以修改a.x
并不会去影响b
,所以打印仍然为2
答案:2 2 2
2. 第二道:
var a = {n:1};
var b = a;
a.x = a = {n:2};
console.log(a.x);
console.log(b.x);
答案在下面思考下哦~
这次我尝试一张图解释:
答案: undefined {n: 2}
4. JS 基础-原型和原型链
三座大山之一,必考!!!
4.1 JavaScript 中的原型,原型链分别是什么?
在 JS 中,原型和原型链是一个很重要的概念,可以说原型本质就是一个对象。它分为两种: 对象的原型和 函数的原型
对象的原型:
- 任何对象都有自己默认的原型(隐式原型),
- 它的作用就是在当前对象查找某一个属性时, 如果找不到, 会在原型上面查找
获取隐式原型的方法:
__proto__
(这个不是规范,是浏览器加的,因为早期没有获取原型对象的方法)Object.getPrototypeOf(obj)
函数的原型:
- 首先,因为函数也是一个对象,所以他会有一个
__proto__
隐示原型 - 其次任何一个函数(非箭头函数)还会有自己的
prototype
属性(显式原型) 获取显示原型的方法:
prototype
作用:
- 当通过
new
操作符调用函数时, 创建一个新的对象 - 这个新的对象的 隐式原型 会指向这个函数的 显式原型
obj.__proto__ = F.prototype
- 当通过
原型链:
上面讲过,隐式原型的作用是 当前对象查找某一个属性时, 如果找不到, 会在原型上面查找,而原型也是一个对象,所以在原型对象上找不到的话,还会去原型对象的对象上找,这样一层一层、以此类推就形成了原型链(prototype chain
)
4.2 如何准确判断一个变量不是数组?
1. instanceof
instanceof
运算符用于检测构造函数的 prototype
属性是否出现在某个实例对象的原型链上
如果让你实现一个 instanceof
应该就很简单了吧?(循环遍历对象的隐式原型知道为 null
或者为 Array
)
let arr = [1, 2];
arr instanceof Array // true
2. toString
let arr = [1, 2];
Object.prototype.toString.call(arr) === '[object Array]'
3. constructor
let arr = [1,2];
arr.constructor === Array; // true
4. isArray
let arr = [1,2];
Array.isArray(arr) // true
4.3 JS 如何实现继承
在 ES6
中可以使用 class + extends
的方式很容易实现继承,而它的本质其实就是通过原型链来实现的
ES6
实现继承:
class Person {
constructor(name) {
this.name = name;
}
sayHello() {
console.log("你好,我是" + this.name);
}
}
class Student extends Person {
constructor(name, sno) {
super(name);
this.sno = sno;
}
readingBooks() {
console.log("我正在读书");
}
}
const stu = new Student("张三", "001");
// Student 子类能调用父类的 sayHello 方法
stu.sayHello();
stu.readingBooks();
ES5
实现继承:
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function () {
console.log("你好,我是", this.name);
};
function Student(name, sno) {
this.name = name;
this.sno = sno;
}
const per = new Person()
Student.prototype = perper;
Student.prototype.readingBooks = function () {
console.log("我正在读书");
};
const stu = new Student("张三", "001");
// Student 子类能调用父类的 sayHello 方法
stu.sayHello();
stu.readingBooks();
ES5
中的继承还会复杂一些,初级面知道这些就够了,如果感兴趣,还可以看下我的这篇文章 js 常见手写题汇总 第 14
章实现继承。
下面我们用一张图来解释原型链继承的原理:
5. JS 基础-作用域和闭包
三座大山之二,不会闭包,基本不会通过
5.1 什么是作用域?什么是自由变量?
作用域 就是即变量(变量作用域又称上下文)和函数生效(能被访问)的区域或集合。
我们一般将作用域分成:
- 全局作用域:任何不在函数中或是大括号中声明的变量,都是在全局作用域下,全局作用域下声明的变量可以在程序的任意位置访问
- 函数作用域:函数作用域也叫局部作用域,如果一个变量是在函数内部声明的它就在一个函数作用域下面。这些变量只能在函数内部访问,不能在函数以外去访问
- 块级作用域:
ES6
引入了let
和const
关键字,和var
关键字不同,在大括号中使用let
和const
声明的变量存在于块级作用域中。在大括号之外不能访问这些变量。
自由变量 就是一个变量在当前作用域没有定义,但被使用了。那这个时候怎么办呢?它会向上级作用域,一层一层依次寻找,知道找到为止。如果到全局作用域都没找到,则报错 xx is not defined
5.2 什么是闭包?闭包会用在哪儿?
闭包是什么?
一个函数和对其周围状态(lexical environment
,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure
)
也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域
在 JavaScript
中,每当创建一个函数,闭包就会在函数创建的同时被创建出来,作为函数内部与外部连接起来的一座桥梁
下面给出一个简单的例子:
function init() {
var name = "Mozilla"; // name 是一个被 init 创建的局部变量
function displayName() { // displayName() 是内部函数,一个闭包
alert(name); // 使用了父函数中声明的变量
}
displayName();
}
init();
使用场景
闭包常常作为函数 参数 和 返回值:
- 作为函数参数:
function print(fn) {
let a = 200;
fn();
}
let a = 100;
function fn() {
console.log(a);
}
print(fn); // 100
- 作为函数的返回值
function create() {
let a = 100;
return function () {
console.log(a);
};
}
let fn = create();
let a = 200;
fn(); // 100
任何闭包的使用场景都离不开这两点:
- 创建私有变量
- 延长变量的生命周期
一般函数的词法环境在函数返回后就 被销毁,但是闭包会 保存对创建时所在词法环境的引用,即便创建时所在的执行上下文被销毁,但创建时所在词法环境依然存在,以达到 延长变量的生命周期的目的
使用闭包的注意点
- 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在
IE
中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。 - 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(
object
)使用,把闭包当作它的公用方法(Public Method
),把内部变量当作它的私有属性(private value
),这时一定要小心,不要随便改变父函数内部变量的值。
5.3 this 的绑定规则有哪些?优先级?
this
关键字是函数运行时自动生成的一个内部对象,只能在函数内部使用,总指向调用它的对象。
根据不同的使用场合,this
有不同的值,主要分为下面几种情况:
1. 默认绑定:
什么情况下使用默认绑定呢?独立函数调用。
- 独立的函数调用我们可以理解成函数没有被绑定到某个对象上进行调用;
案例一:普通函数调用
- 该函数直接被调用,并没有进行任何的对象关联;
- 这种独立的函数调用会使用默认绑定,通常默认绑定时,函数中的
this
指向全局对象(window
);
function foo() {
console.log(this); // window
}
foo();
案例二:函数调用链(一个函数又调用另外一个函数)
- 所有的函数调用都没有被绑定到某个对象上;
// 2.案例二:
function test1() {
console.log(this); // window
test2();
}
function test2() {
console.log(this); // window
test3()
}
function test3() {
console.log(this); // window
}
test1();
案例三:将函数作为参数,传入到另一个函数中
function foo(func) {
func()
}
function bar() {
console.log(this); // window
}
foo(bar);
我们对案例进行一些修改,考虑一下打印结果是否会发生变化:
- 这里的结果依然是
window
,为什么呢? - 原因非常简单,在真正函数调用的位置,并没有进行任何的对象绑定,只是一个独立函数的调用;
function foo(func) {
func()
}
var obj = {
name: "why",
bar: function() {
console.log(this); // window
}
}
foo(obj.bar);
2. 隐式绑定:
另外一种比较常见的调用方式是通过某个对象进行调用的:
- 也就是它的调用位置中,是通过某个对象发起的函数调用。
案例一:通过对象调用函数
foo
的调用位置是obj.foo()
方式进行调用的- 那么
foo
调用时this
会隐式的被绑定到obj
对象上
function foo() {
console.log(this); // obj对象
}
var obj = {
name: "why",
foo: foo
}
obj.foo();
案例二:案例一的变化
- 我们通过
obj2
又引用了obj1
对象,再通过obj1
对象调用foo
函数; - 那么
foo
调用的位置上其实还是obj1
被绑定了this
;
function foo() {
console.log(this); // obj1 对象
}
var obj1 = {
name: "obj1",
foo: foo
}
var obj2 = {
name: "obj2",
obj1: obj1
}
obj2.obj1.foo();
案例三:隐式丢失
结果最终是 window
,为什么是 window
呢?
- 因为
foo
最终被调用的位置是bar
,而bar
在进行调用时没有绑定任何的对象,也就没有形成隐式绑定; - 相当于是一种默认绑定;
function foo() {
console.log(this);
}
var obj1 = {
name: "obj1",
foo: foo
}
// 讲obj1的foo赋值给bar
var bar = obj1.foo;
bar();
3. 显示绑定
隐式绑定有一个前提条件:
- 必须在调用的
对象内部
有一个对函数的引用(比如一个属性); - 如果没有这样的引用,在进行调用时,会报找不到该函数的错误;
- 正是通过这个引用,间接的将this绑定到了这个对象上;
如果我们不希望在 对象内部 包含这个函数的引用,同时又希望在这个对象上进行强制调用,该怎么做呢?
JavaScript
所有的函数都可以使用call
和apply
方法(这个和Prototype
有关)。- 它们两个的区别这里不再展开;
- 其实非常简单,第一个参数是相同的,后面的参数,
apply
为数组,call
为参数列表;
- 这两个函数的第一个参数都要求是一个对象,这个对象的作用是什么呢?就是给
this
准备的。 - 在调用这个函数时,会将
this
绑定到这个传入的对象上。
因为上面的过程,我们明确的绑定了 this
指向的对象,所以称之为 显示绑定。
案例一:call
、apply
通过 call
或者 apply
绑定 this
对象
- 显示绑定后,
this
就会明确的指向绑定的对象
function foo() {
console.log(this);
}
foo.call(window); // window
foo.call({name: "why"}); // {name: "why"}
foo.call(123); // Number对象,存放 123
案例二:bind
函数
如果我们希望一个函数总是显示的绑定到一个对象上,我们可以使用 bind
函数:
function foo() {
console.log(this);
}
var obj = {
name: "why"
}
var bar = foo.bind(obj);
bar(); // obj对象
bar(); // obj对象
bar(); // obj对象
案例三:内置函数
有些时候,我们会调用一些 JavaScript
的内置函数,或者一些第三方库中的内置函数。
- 这些内置函数会要求我们传入另外一个函数;
- 我们自己并不会显示的调用这些函数,而且
JavaScript
内部或者第三方库内部会帮助我们执行; - 这些函数中的
this
又是如何绑定的呢?
// 1. setTimeout中会传入一个函数,这个函数中的this通常是window
setTimeout(function() {
console.log(this); // window
}, 1000);
// 2. forEach map filter 等高阶函数 this 通常指向 window对象,但我们可以通过第二个参数改变
var names = ["abc", "cba", "nba"];
names.forEach(function(item) {
console.log(this); // 三次window
});
var names = ["abc", "cba", "nba"];
var obj = {name: "why"};
names.forEach(function(item) {
console.log(this); // 三次obj对象
}, obj);
4. new
绑定
JavaScript
中的函数可以当做一个类的构造函数来使用,也就是使用 new
关键字。
使用 new
关键字来调用函数时,会执行如下的操作:
1.创建一个全新的对象;
2.这个新对象会被执行 Prototype
连接;
3.这个新对象会绑定到函数调用的 this
上 (this
的绑定在这个步骤完成);
4.如果函数没有返回其他对象,表达式会返回这个新对象;
// 创建Person
function Person(name) {
console.log(this); // Person {}
this.name = name; // Person {name: "why"}
}
var p = new Person("why");
console.log(p);
5. 优先级
new绑定
> 显示绑定(bind)
> 隐式绑定
> 默认绑定
PS: new
绑定后可以使用 bind
但是 bind
不会生效。 new
绑定后使用 call
和 apply
会报错。
5. 规则之外
bind
绑定一个null
或者undefined
无效- 间接函数引用
function foo() {
console.log(this);
}
var obj1 = {
name: "obj1",
foo: foo
};
var obj2 = {
name: "obj2"
}
obj1.foo(); // obj1对象
// 赋值(obj2.foo = obj1.foo)的结果是foo函数
// foo函数被直接调用,那么是默认绑定;
(obj2.foo = obj1.foo)(); // window
ES6
箭头函数:箭头函数不使用this
的四种标准规则(也就是不绑定this
),而是根据外层作用域来决定this
。
6. JS 基础-异步
三座大山之三,必考!!!
6.1 同步 和 异步 的区别
JS
是一门单线程的编程语言,这就意味着一个时间里只能处理一件事,也就是说 JS
引擎一次只能在一个线程里处理一条语句。(浏览器和 nodejs
已经支持 js
启动进程,如 web worker
)
虽然单线程简化了编程代码,因为这样咱们不必太担心并发引出的问题,这也意味着在阻塞主线程的情况下执行长时间的操作,如网络请求。
想象一下从 API
请求一些数据,根据具体的情况,服务器需要一些时间来处理请求,同时阻塞主线程,使网页长时间处于无响应的状态。这就是引入异步 JS
的原因。使用异步 (如 回调函数、promise
、async/await
),可以不用阻塞主线程的情况下长时间执行网络请求。
总结:基于 js
单线程本质,同步会阻塞代码执行,异步不会阻塞代码执行。
6.2 异步的应用场景
- 网络请求,如
ajax
加载图片 - 定时任务,
setTimeout
6.3 你是怎么理解ES6中 Promise的?
1. 介绍
Promise
,译为承诺,是异步编程的一种解决方案,比传统的解决方案(回调函数)更加合理和更加强大。
Promise
的出现主要解决了用回调函数处理多层异步操作出现的回调地狱问题
// 1. 经典的回调地狱
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('得到最终结果: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
// 2. 链式操作减低了编码难度 代码可读性明显增强
doSomething().then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log('得到最终结果: ' + finalResult);
})
.catch(failureCallback);
promise
对象仅有三种状态:pending(进行中)
fulfilled(已成功)
rejected(已失败)
- 对象的状态不受外界影响,只有异步操作的结果,可以决定当前是哪一种状态
- 一旦状态改变(从
pending
变为fulfilled
和从pending
变为rejected
),就不会再变,任何时候都可以得到这个结果
2. 用法
创建一个 Promise
// 1. Promise对象是一个构造函数,用来生成Promise实例
// resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”
// reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”
const promise = new Promise(function(resolve, reject) {});
实例方法:
then()
:then
是实例状态发生改变时的回调函数,第一个参数是resolved
状态的回调函数,第二个参数是rejected
状态的回调函数catch()
:catch()
方法是.then(null, rejection)
或.then(undefined, rejection)
的别名,用于指定发生错误时的回调函数finally()
:finally()
方法用于指定不管Promise
对象最后状态如何,都会执行的操作
构造函数方法:
- all()
- race()
- allSettled()
- resolve():将现有对象转为 Promise 对象
- reject():返回一个新的 Promise 实例,该实例的状态为rejected
ps:all
race
allSettled
这三个方法都能将多个 promise
实例转换为 1
个 promise
实例,区别就是 all
只有当所有实例状态都变为 fulfilled
最终返回的实例状态才会变为 fulfilled
。race
则返回最快改变状态的实例,allSettled
则会等所有实例状态改变才会改变。
6.4 手写 Promise 加载一张图片
const preloadImage = function (path) {
return new Promise(function (resolve, reject) {
const image = new Image();
image.onload = () => { resolve(image) };
image.onerror = () => {
const err = new Error(`图片加载失败${path}`)
reject(err)
};
image.src = path;
});
};
7. JS 异步进阶
关于异步还有更多的问题,很重要
7.1 什么是事件循环 event loop?
- 因为
js
是单线程的,为了防止代码阻塞会将代码分成同步和异步 - 同步代码交给
js
引擎执行,异步代码交给宿主环境(浏览器、node) - 同步代码放入执行栈中,异步代码等待时机成熟送入任务队列排队
- 执行栈执行完毕,会去任务队列看是否有异步任务,有就送到执行栈执行,反复循环查看执行代码,这个过程就是事件循环(
event loop
)
7.2 什么是宏任务和微任务,它们的区别?
上题讲到 js
将代码分成同步和异步,而在异步任务中又分 宏任务(macro-task
)和微任务(micro-task
)。 宏任务是由宿主(浏览器、node
)发起的,微任务是由 js
引擎发起的
宏任务大概包括
- script(整体代码)
- setTimeout
- setInterval
- setImmediate
- I/O
- UI render
微任务大概包括
- process.nextTick
- Promise
- Async/Await(实际就是promise)
- MutationObserver(html5新特性)
宏任务和微任务的执行过程
- 先执行同步代码
- 后执行微任务的异步代码
- 再执行宏任务的异步代码
如下图所示:
下面有个小例子:
setTimeout(() => {
console.log(1);
});
Promise.resolve().then(() => {
console.log(2);
});
console.log(3);
代码最终执行: 3 2 1
7.3 async/await 语法
用法很简单,这里不说了。
你可以把 await
后面的代码理解为是放在 Promise.then
中,看上去相当于将链式的调用变成了同步的执行
关于async/await 的本质 最好去理解一下,其实就是 Promise
和 Generator
的语法糖
7.4 手写 Promise
详见 JavaScript 常见手写题汇总 第 15
章
8. JS-Web-API-DOM
学会DOM,才能具备网页开发的基础
8.1 DOM 是什么?
DOM
:Document Object Model 指的是文档对象模型,它指的是把文档当做一个对象,这个对象主要定义了处理网页内容的方法和接口。
DOM 的本质就是一颗树
8.2 DOM 节点的操作
获取 DOM
节点
- getElementById
- getElementByClassName
- getElementByTagName
- querySeletorAll
DOM
节点的 property
通过 js
对象属性的方式来获取或修改 DOM
节点
const pList = document.querySelectorAll('p')
const p = pList[0]
console.log(p.style.width) // 获取样式
p.style.width = '100px' // 修改样式
console.log(p.className) // 获取 class
p.className = 'p1' // 修改 class
// 获取 nodeName 和 nodeType
console.log(p.nodeName)
console.log(p.nodeType)
DOM
节点的 attribute
通过 getAttribute
setAttribute
这种 API
的方式来修改 DOM
节点
const pList = document.querySelectorAll('p')
const p = pList[0]
p.setAttribute('data-name', 'coder') // 设置值
console.log(p.getAttribute('data-name')) // 获取值
property
和 attribute
形式都可以修改节点的属性,但是对于新增或删除的自定义属性,能在 html
的 dom
树结构上体现出来的,就必须要用到 attribute
形式了。
8.3 DOM 结构操作
新增插入节点:
<div id="div1">div1</div>
<div id="div2">div2</div>
<p id="p2">这是 p2 标签</p>
<script>
const div1 = document.getElementById("div1");
const div2 = document.getElementById("div2");
// 新建节点
const p1 = document.createElement("p");
p1.innerHTML = "这是新的 p 标签";
// 插入节点
div1.appendChild(p1);
// 移动节点
const p2 = document.getElementById("p2");
div2.appendChild(p2);
</script>
获取子元素列表,获取父元素
// 获取父元素
console.log(p1.parentNode);
// 获取子元素列表
console.log(div2.childNodes);
删除子元素
// 删除子元素
div2.removeChild(div2.childNodes[0]);
8.4 如何优化 DOM 性能
DOM
操作非常昂贵,避免频繁的 DOM
操作
- 对
DOM
操作做缓存 - 将频繁
DOM
操作改为一次性操作
9. JS-Web-API-BOM
内容虽然不多,但是你不能不会
9.1 什么 BOM ?
BOM
:Browser Object Model 指的是浏览器对象模型,它指的是把浏览器当做一个对象来对待,这个对象主要定义了与浏览器进行交互的法和接口。BOM
的核心是 window
,而 window
对象具有双重角色,它既是通过 js
访问浏览器窗口的一个接口,又是一个 Global
(全局)对象。这意味着在网页中定义的任何对象,变量和函数,都作为全局对象的一个属性或者方法存在。window
对象含有 location
对象、navigator
对象、screen
对象等子对象,并且 DOM
的最根本的对象 document
对象也是 BOM
的 window
对象的子对象。
9.2 如何识别浏览器类型
navigator.userAgent
简称 ua,可以从 ua 里拿到浏览器信息
9.3 拆解 url 各部分
location:
- href
- protocol
- pathname
- search
- hash
10. JS-Web-API-事件
事件不会,等于残废,必考!必考!
10.1 谈谈你对事件冒泡和捕获的理解
事件冒泡和事件捕获分别由微软和网景公司提出,这两个概念都是为了解决页面中事件流(事件发生顺序)的问题。
<div id="outer">
<p id="inner">Click me!</p>
</div>
上面的的两个元素,如果点击 inner,他们的执行顺序是什么呢?
- 事件冒泡:先执行 inner 的监听事件在执行 outer 的监听事件
- 事件捕获:先执行 outer 的监听事件在执行 inner 的监听事件
其实这个很好理解,冒泡就是从一个泡泡从水底往上冒当然是里面的先执行啦。
至于为什么会有这两种情况,这就要谈到网景和微软的战争了,两家公司的理念不同。网景主张捕获方式,微软主张冒泡方式。后来 w3c
将两者都保留了下来。
addEventListener
的第三个参数就是为冒泡和捕获准备的。第三个参数设置为 true
可以将让当前元素绑定的事件先于里面的元素绑定事件执行。默认是 false
10.2 什么是 事件代理
事件代理(Event Delegation
)也称之为事件委托。是 JavaScript
中常用绑定事件的常用技巧。
顾名思义,事件代理
即是把原本需要绑定在子元素的响应事件委托给父元素,让父元素担当事件监听的职务。
事件代理的原理是DOM元素的事件冒泡。
11. JS-Web-API-Ajax
每个工程师必须熟练掌握的技能
11.1 什么是 Ajax ?
Ajax
(全称 Asynchronous JavaScript And XML
) 翻译过来就是 异步的 Javascript 和 XML
AJAX
是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。
随着谷歌搜索建议功能在 2005 的发布,AJAX 开始流行起来。
11.2 手写一个简易的 Ajax
网页中实现 Ajax
最核心的 API
就是 XMLHttpRequest
,如果不知道这个就别谈实现了。
function ajax(url) {
const xhr = new XMLHttpRequest();
xhr.open("get", url, true);
xhr.onreadystatechange = function () {
// 异步回调函数
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.info("响应结果", xhr.response)
}
}
}
xhr.send(null);
}
11.3 浏览器的同源策略是什么??
同源策略(Same origin policy
)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说 Web
是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
它的核心就在于它认为自任何站点装载的信赖内容是不安全的。当被浏览器半信半疑的脚本运行在沙箱时,它们应该只被允许访问来自同一站点的资源,而不是那些来自其它站点可能怀有恶意的资源。
所谓同源是指:域名、协议、端口相同。
另外,同源策略又分为以下两种:
DOM
同源策略:禁止对不同源页面DOM
进行操作。这里主要场景是iframe
跨域的情况,不同域名的iframe
是限制互相访问的。XMLHttpRequest
同源策略:禁止使用XHR
对象向不同源的服务器地址发起HTTP
请求。
11.4 什么是跨域?
跨域本质是浏览器基于同源策略的一种安全手段
同源策略(Sameoriginpolicy
),是一种约定,它是浏览器最核心也最基本的安全功能
所谓同源(即指在同一个域)具有以下三个相同点
- 协议相同(protocol)
- 主机相同(host)
- 端口相同(port)
反之非同源请求,也就是协议、端口、主机其中一项不相同的时候,这时候就会产生跨域
一定要注意跨域是浏览器的限制,你用抓包工具抓取接口数据,是可以看到接口已经把数据返回回来了,只是浏览器的限制,你获取不到数据。用
postman
请求接口能够请求到数据。这些再次印证了跨域是浏览器的限制。
11.5 如何实现跨域请求
首先跨域是因为浏览器的同源策略造成的,他是浏览器的一种安全机制。跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了
实现跨域常见的方案:
- jsonp
- nginx反向代理
- webpack devServer代理
- cors跨源资源共享
其中 jsonp
由于其局限性,以及对比其他方案的效果。此处不做介绍。
1. nginx方向代理
nginx
反向代理常用在开发环境及线上环境。通过拦截转发请求来处理跨域问题。
假如现在前端项目运行在 8080
端口,而实际后端项目的地址为 https://1.1.1.1:9000
,需要拦截前缀为 api
的请求,此时 nginx
配置为:
server {
listen 8080 default_server;
location /api {
proxy_pass https://1.1.1.1:9000;
}
}
假如现在有个接口为 /api/test
,在没有做转发前为 http://localhost:8080/api/test
,实际接口位置为 https://1.1.1.1:9000/api/test
.结果转发为实际接口位置。
2. webpack devserver代理webpack devserver
代理用在开发环境。
配置如下:
devServer({
proxy: {
'/api': {
target: 'https://1.1.1.1:9000',
changeOrigin: true,
pathRewrite: { '^/api': 'api' },
},
}
})
3. cors跨源资源共享(服务端设置)
跨源资源共享 (CORS
) (或通俗地译为跨域资源共享)是一种基于 HTTP
头的机制,该机制通过允许服务器标示除了它自己以外的其它 origin
(域,协议和端口),这样浏览器可以访问加载这些资源。
Access-Control-Allow-Origin: 'xxx'
可以通过服务端设置 Access-Control-Allow-Origin
字段的白名单来处理跨域问题。
如果在此情况下,发送请求时需要带上cookie的话,则需要配置Access-Control-Allow-Credentials,同时客户端需要同步设置xhr.withCredentials = true;,两者缺一不可
11.6 ajax fetch axios 的区别
ajax
是js
异步技术的术语,早期相关的api
是xhr
,它是一个术语。fetch
是es6
新增的用于网络请求标准api
,它是一个api
。(是es6
用来代替xhr
的,xhr
很不好用)axios
是用于网络请求的第三方库,它是一个库。
12. JS-Web-API-存储
内容虽然不多,但不可不会
12.1 描述 cookie sessionStorage localStorage 的区别
cookie
本身是用于浏览器和 server
通讯的,他是被借用到本地用于存储的,因为后两者是在 H5
后才提出来的( 2010年左右),我们可以通过 document.cookie = 'xxx'
来改变 cookie
。
cookie
的缺点:
- 存储大小最大
4kb
(因为cookie
本身就不是用来做存储的) http
请求时需要发送到服务端,增加请求数据量- 只能用
document.cookie
来修改,太过简陋
localStorage
和 sessionStorage
:
- 它俩是
H5
专门为了存储设计的,最大可存5M
左右 API
更简洁:getItem
setItem
- 不会随着
http
被发送出去 localStorage
数据会永久存储,除非代码或者手动删除,sessionStorage
数据只存在于房钱会话,浏览器关闭则清除。一般localStorage
会更多一些
13. HTTP 面试题
前后端分离的时代,网络请求是前端的生命线
13.1 http 常见的状态码有哪些?
分类:
- 1xx 服务器收到请求
- 2xx 请求成功,如 200
- 3XX 重定向,如 302
- 4xx 客户端错误,如 404
- 5xx 服务端错误,如 500
常见状态码:
1XX: 提供信息
- 100
Continue
情景:客户端向服务端传很大的数据,这个时候询问服务端,如果服务端返回100,客户端就继续传 (历史,现在比较少了) - 101
Switching Protocols
协议切换。比如下面这种:
- 100
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
告诉客户端把协议切换为 Websocket
2xx: 成功
- 200
Ok
正常的返回成功 通常用在GET
- 201
Created
已创建 通常用在POST
- 202
Accepted
已接收 比如发送一个创建POST
请求,服务端有些异步的操作不能马上处理先返回202
,结果需要等通知或者客户端轮询获取 - 203
Non-Authoritative Infomation
非权威内容 原始服务器的内容被修改过 - 204
No Content
没有内容 一般PUT
请求修改了但是没有返回内容 - 205
Reset Content
重置内容 - 206
Partial Content
服务端下发了部分内容
- 200
3XX: 重定向
- 300
Multiple Choices
用户请求了多个选项的资源(返回选项列表) - 301
Moved Permanently
永久转移 - 302
Found
资源被找到(以前是临时转移)不推荐用了302
拆成了303
和307
- 303
See Other
可以使用GET
方法在另一个URL
找到资源 - 304
Not Modified
没有修改 - 305
Use Proxy
需要代理 - 307
Temporary Redirect
临时重定向 (和303
的区别是,307
使用原请求的method
重定向资源,303
使用GET
方法重定向资源) - 308
Permanent Redirect
永久重定向 (和301
区别是 客户端接收到308
后,之前是什么method
,之后也会沿用这个method
到新地址。301
,通常给用户会向新地址发送GET
请求)
- 300
4XX: 客户端错误
- 400
Bad Request
请求格式错误 - 401
Unauthorized
没有授权 - 402
Payment Required
请先付费 - 403
Forbidden
禁止访问 - 404
Not Found
没有找到 - 405
Method Not Allowed
方法不允许 - 406
Not Acceptable
服务端可以提供的内容和客户端期待的不一样
- 400
5XX: 服务端错误
- 500
Internal Server Error
内部服务器错误 - 501
Not Implemented
没有实现 - 502
Bad Gateway
网关错误 - 503
Service Unavailable
服务不可用 (内存用光了,线程池溢出,服务正在启动) - 504
Gateway Timeout
网关超时 - 505
HTTP Version Not Supported
版本不支持
- 500
面试的时候常见该记住的有:101
200
201
301
302
304
403
404
500
502
504
规范就是一个约定,要求大家跟着执行,不要违反规范,例如 IE
浏览器
13.2 http 常见的 header 头
1. Content-Length
发送给接收者的 Body 内容长度(字节)
- 一个 byte 是 8bit
- UTF-8 编码的字符 1-4 个字节、
示例:Content-Length: 348
2. User-Agent
帮助区分客户端特性的字符串
- 操作系统 - 浏览器 - 制造商(手机类型等) - 内核类型 - 版本号
示例:User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36
3. Content-Type
帮助区分资源的媒体类型(Media Type/MIME Type)
- text/html
- text/css
- application/json
- image/jpeg
示例:Content-Type: application/x-www-form-urlencoded
4. Origin
描述请求来源地址
- scheme://host:port
- 不含路径
- 可以是null
示例: Origin: https://yewjiwei.com
5. Accept
建议服务端返回何种媒体类型(MIME Type)
- /代表所有类型(默认)
- 多个类型用逗号隔开
衍生的还有
- Accept-Charset 能够接受的字符集 示例:Accept-Charset: utf-8
- Accept-Encoding 能够接受的编码方式列表 示例:Accept-Encoding: gzip, deflate
- Accept-Language 能够接受的回应内容的自然语言列表 示例:Accept-Language: en-US
示例:
- Accept: text/plain
- Accept-Charset: utf-8
- Accept-Encoding: gzip, deflate
6. Referer
告诉服务端打开当前页面的上一张页面的URL;如果是ajax请求那么就告诉服务端发送请求的URL是什么
- 非浏览器环境有时候不发送Referer
- 常常用户行为分析
7. Connection
决定连接是否在当前事务完成后关闭
- HTTP1.0默认是 close
- HTTP1.1后默认是 keep-alive
13.3 什么是 RESTFUL API ?
Restful API
是一种新的 API
设计方法(早已推广)
- 传统
API
设计:把每一个url
当做一个功能 Restful API
设计:把每个url
当做一个唯一的资源
传统
/api/list?pageIndex=2
Restful API
/api/list/2
13.4 描述一下 http 缓存机制
HTTP
缓存即是浏览器第一次向一个服务器发起 HTTP
请求后,服务器会返回请求的资源,并且在响应头中添加一些有关缓存的字段如:cache-control
,expires
, last-modifed
,ETag
, Date
,等,之后浏览器再向该服务器请求资源就可以视情况使用强缓存和协商缓存,
- 强缓存:浏览器直接从本地缓存中获取数据,不与服务器进行交互,
- 协商缓存:浏览器发送请求到服务器,服务器判断是否可使用本地缓存,