# 面试
**Repository Path**: andypoplar/interview
## Basic Information
- **Project Name**: 面试
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2021-05-08
- **Last Updated**: 2021-05-08
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
## 面试
### 1. JavaScript基础
- 讶羽github1--9,13,[链接](https://github.com/mqyqingfeng/Blog)
> 上下文:
>
> 1. 全局上下文的变量对象初始化是全局对象
> 2. 函数上下文的变量对象初始化只包括 Arguments 对象
> 3. 在进入执行上下文时会给变量对象添加形参、函数声明、变量声明等初始的属性值
> 4. 在代码执行阶段,会再次修改变量对象的属性值
> 作用域链:多个执行上下文的变量对象构成的链表就叫做作用域链。
>
> 1. 函数的作用域在函数定义的时候就决定了
> this:
>
> Reference :只存在于规范里的抽象类型
>
> 1. base value
> 2. referenced name
> 3. strict reference
> 4. this一般有几种调用场景
> var obj = {a: 1, b: function(){console.log(this);}}
> 1、作为对象调用时,指向该对象 obj.b(); // 指向obj
> 2、作为函数调用, var b = obj.b; b(); // 指向全局window
> 3、作为构造函数调用 var b = new Fun(); // this指向当前实例对象
> 4、作为call与apply调用 obj.b.apply(object, []); // this指向当前的object
> 闭包:即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回)
- 手写new原理
```js
function mynew(Parent, ...test) {
let result = {};
result.__proto__ = Parent.prototype;
let resultParent = Parent.apply(result, test);
if (typeof resultParent === 'object' && resultParent !== null || typeof resultParent === 'function') {
return resultParent
}
return result
}
function Parent(name, age) {
this.name = name;
this.age = age;
}
let child = mynew(Parent, '21', 'andy', )
console.log(child);
```
- call,apply,bind区别
> call和apply的用法几乎相同(改变this,立即执行),当需要传递多个参数时,call只能一个一个传,apply可以传一个数组
>
> bind只改变this,参数只能一个一个传,不会立即执行
- ES6数组方法(Array.form(),flat)和属性
> Array,from()
>
> 1.字符串转为数组
>
> ```js
> Array.from('foo');
> // [ "f", "o", "o" ]
> ```
>
> 2. 把set转为数组
>
> const set = new Set(['foo', 'bar', 'baz', 'foo']);
>
> Array.from(set); // [ "foo", "bar", "baz" ]
>
> 3. 把arguments转为数组
>
> 1、该类数组对象必须具有length属性,用于指定数组的长度。如果没有length属性,那么转换后的数组是一个空数组。
>
> 2、该类数组对象的属性名必须为数值型或字符串型的数字
>
> ```js
> function f() {
> return Array.from(arguments);
> }
>
> f(1, 2, 3);
>
> // [ 1, 2, 3 ]
> ```
>
> 4. `Array.from`还可以接受第二个参数,作用类似于数组的`map`方法,用来对每个元素进行处理,将处理后的值放入返回的数组。如下:
>
> ```js
> let arr = [12,45,97,9797,564,134,45642]
> console.log(Array.from(arr, item => item + 1)) // [ 13, 46, 98, 9798, 565, 135, 45643 ]
> ```
> Array.flat
>
> 1. 使用 Infinity,可展开任意深度的嵌套数组
>
> ```js
> var arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
> arr4.flat(Infinity);
> // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
> ```
>
> 2. flat() 方法会移除数组中的空项:
>
> ```js
> var arr4 = [1, 2, , 4, 5];
> arr4.flat();
> // [1, 2, 4, 5]
> ```
>
> Array.of()
>
> Array.of() 和 Array构造函数之间的区别在于处理整数参数:Array.of(7) 创建一个具有单个元素 **7** 的数组,而 **`Array(7)`** 创建一个长度为7的空数组(**注意:**这是指一个有7个空位(empty)的数组,而不是由7个`undefined`组成的数组)
>
> ```js
> Array.of(7); // [7]
> Array.of(1, 2, 3); // [1, 2, 3]
>
> Array(7); // [ , , , , , , ]
> Array(1, 2, 3); // [1, 2, 3]
> ```
>
>
- 继承(原型链,构造,组合,寄生,寄生组合,原型式各种优缺点)
- 手写Promise
> 什么是promise?
Promise是Js异步编程中新的解决方案
> 为什么要用promise?
1. 指定回调函数更加灵活
2. 支持链式调用,解决回调地狱
> promise.then()返回的新promise的结果状态由什么决定?
1. 如果抛出异常,新promise的状态为reject,reason为抛出的异常
2. 如果返回一个值,新promise的状态为resolve,value为返回的值
3. 如果返回一个promise,那么新的promise的结果由返回的promise结果决定
> promise如何串连多个操作任务?
1. promise的then()返回一个新的promise
2. 通过then的链式调用串联多个同步/异步任务
> promise异常传/穿透
1. 当使用promise的then链式调用时,可以在最后指定失败问题
2. 前面任何操作出了异常,都会传到最后失败的回调中处理
> 中断promise链?
1. 当使用promise的then链式调用时,在中间中断,不再调用后面的回调函数
2. 办法:在回调函数中返回一个pending状态的promise对象
> 手写promise.all
>
> 1. 如何知道所有promise都成功了??
> 2. 为什么不能成功一个就忘成功的数组里面push一个?因为promise可能有setTimeout的
> 3. 只写了一个reject,如果后面还有reject会覆盖前面的嘛? 不会,状态只能改一次
```js
/**
* Promise函数对象的all方法
* 返回一个Promise,只有当所有promise都成功时才成功,否则失败
*/
Promise.all = function (promises) {
// 用来保存所有成功value的数组并指定长度
const values = new Array(promises.length);
// 用来保存成功promise的数量
let resolvedCount = 0;
return new Promise((resolve, reject) => {
// 遍历获取每个promise的结果
promises.forEach((p, index) => {
// 这里用Promise包装下,因为promise.all中可以传数值
Promise.resolve(p).then(
value => {
resolvedCount++;
// p成功,将成功的value保存values
values[index] = value;
// 如果全部成功了,将return的promise改变成功
if (resolvedCount === promises.length) {
resolve(values)
}
},
reason => { // 只要一个失败了,return的promise就都失败
reject(reason)
}
)
})
})
}
```
- await/async
规则
> **1. 凡是在前面添加了async的函数在执行后都会自动返回一个Promise对象**
>
> **2. await必须在async函数里使用,不能单独使用**
>
> **3. await后面可以跟Promise对象,也可以跟数值**
错误处理
> 1. test().then(value=>{}).catch(error=>{})
> 2. 在另一个async中处理
> 3. 
并行串行
1. 普通并行
> ```js
> const infoPromise = getUserInfo();
> const userPromise = getUserInfo();
> const info = await infoPromisex;
> const user = await userPromise;
> ```
2. Promise.all()并行
```js
async function getUserInfo() {
return "get"
}
async function Login() {
return "success"
}
const showInfo = async () => {
console.time(showInfo);
const [userInfo,isLogin] = await Promise.all([getUserInfo(),Login()])
console.log(userInfo);
console.log(isLogin);
console.timeEnd(showInfo);
}
showInfo()
```
3. await在for循环中的应用
串行

并行:

- 事件冒泡和事件捕获以及事件委托 ✔
- 手写深拷贝
> 考虑:
>
> 1. 参数是否是对象,不是则返回其本身
> 2. 是数组还是对象
> 3. 考虑循环引用问题
> 4. 需要判断每一个属性的类型,如果是对象类型,需要递归处理
>
> ```js
> function isObject(obj) {
> const result = Object.prototype.toString.call(obj).slice(8, -1)
> return result === 'Object' || result === 'Array'
> }
>
> function deepClone(target) {
> if (!isObject(target) && target !== null) return target;
>
> const result = Array.isArray(target) ? [] : {};
>
> Reflect.ownKeys(target).forEach(key => {
> if (isObject(target[key])) {
> result[key] = deepClone(target[key])
> } else {
> result[key] = target[key]
> }
> })
> return result
> }
> ```
>
> 采用`Reflect.ownKeys(target).forEach(key => {})`此方式的优点是:
>
> - 只遍历元素自身的属性, 不会遍历原型链上的
> - 可以遍历出类型为`symbol`类型的属性
> - 它与`Object.keys()`相似, 但是它不会受`enumerable`影响
>
> for..in:
>
> - 遍历原型链属性
> - 遍历不了symbol属性
>
> **`WeakMap`** 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。WeakMap 的 key 只能是 `Object` 类型
- js的垃圾回收机制?引用计数有什么本质问题?
> 标记清除:当变量进入执行环境时标记为“进入环境”,当变量离开执行环境时则标记为“离开环境”,被标记为“进入环境”的变量是不能被回收的,因为它们正在被使用,而标记为“离开环境”的变量则可以被回收
>
> 引用计数:统计引用类型变量声明后被引用的次数,当次数为 0 时,该变量将被回收
>
> **但是引用计数的方式,有一个相对明显的缺点——循环引用**
>
> ```js
> function func5 () {
> let f = {}
> let g = {}
> f.prop = g
> g.prop = f
> // 由于 f 和 g 互相引用,计数永远不可能为 0
> }
> ```
>
>
- 什么是箭头函数?箭头函数中this指向哪里?与普通函数的区别
> 箭头函数是普通函数的简写,箭头函数的this是在函数定义时候决定的
> 1. 没有arguments 2. 不可以new(没有自己的this,prototype属性) 3. this是定义时存在
- 有哪些方法识别对象类型?typeof、instanceof、Object.prototype.toString.call(xx)✔
- ES6的set与map?对symbol有了解吗?
> **`Set`** 对象允许你存储任何类型的唯一值,无论是[原始值](https://developer.mozilla.org/zh-CN/docs/Glossary/Primitive)或者是对象引用。
>
> **`Map`** 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者[原始值](https://developer.mozilla.org/zh-CN/docs/Glossary/Primitive)) 都可以作为一个键或一个值。
>
> **symbol**:
>
> 1. 原始数据类型,表示独一无二的值。
> 2. .Symbol 值通过Symbol函数生成,可以作为对象的属性名使用,保证不会与其他属性名产生冲突;
>
> ```js
> let s = Symbol();
> typeof s // symbol
> ```
>
> 3. Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要用于区分不同的 Symbol 变量;
>
> ```js
> let s1 = Symbol('a');
> let s2 = Symbol('b');
>
> s1.toString() // 'Symbol(a)'
> s2.toString() // 'Symbol(b)'
> ```
>
> 4. Symbol 值不能与其他类型的值进行运算,但可以转为布尔值,但是不能转为数值;
>
> ```js
> let s = Symbol();
> s + '2' // Cannot convert a Symbol value to a string
> Boolean(s) // true
> !s // false
> ```
>
> 5. 作为对象属性名时,不能用obj.symbolname调用
> 6. Symbol 作为属性名,不会被常规方法遍历得到,即该属性不会出现在for...in、for...of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回,但是,它并不是私有属性,可以使用 Object.getOwnPropertySymbols 方法,可以获取指定对象的所有 Symbol 属性名
- 手写`instanceof`
```js
function myInstanceof(left, right) {
if (typeof left !== "object" || left == null) return false
// Object.getPrototypeOf可以获得对象的__proto__指向的对象
let proto = Object.getPrototypeOf(left);
while (true) {
// 找到尽头还没找到
if (proto === null) return false
// 找到之后
if (proto === right.prototype) return true
// 没找到继续找
proto = Object.getPrototypeOf(proto)
}
}
console.log(myInstanceof(undefined, String)); // false
console.log(myInstanceof(new String('aaa'), String)); // true
```
- class的extend,super的用途;
> extend:
>
> - `class`可以通过`extends`关键字实现继承父类的所有属性和方法
> - 若是使用了`extends`实现继承的子类内部没有`constructor`方法,则会被默认添加`constructor`。
>
> super:
>
> - 当做函数使用:在 `constructor` 中必须调用 `super` 方法,因为子类没有自己的 `this` 对象,而是继承父类的 `this` 对象。
> - 当做对象使用:在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
- 变量提升,class也有变量提升吗,为什么没有(面试官提示extends);
> 不存在,extends有关系吧
- 手写一个解析url参数的函数;
- 隐式转换✔
- undefined和null区别
> **null表示"没有对象",即该处不应该有值。undefined表示缺少值,此处应该有值,**
>
> undefined:情况
>
> (1)变量被声明了,但没有赋值时,就等于undefined。
>
> (2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。
>
> (3)对象没有赋值的属性,该属性的值为undefined。
>
> (4)函数没有返回值时,默认返回undefined。
- ES6和CommonJs区别
> ES6输出的是值的引用,CommonJS输出的是值的拷贝(浅拷贝)
>
> ES6可以单独加载其中某个方法,CommonJS加载的是整个模块
- 说说EventLoop
> 1. 一开始整个脚本就是一个宏任务
>
> 2. 执行过程中同步代码直接执行,宏任务放入宏任务队列,微任务放入微任务队列
>
> 3. 当前宏任务执行完出队,检查微任务队列,如果有则执行
>
> 4. 再次执行队首新的宏任务,回到2,知道宏任务和微任务队列都为空
>
> ```js
> Promise.resolve().then(()=>{
> console.log('Promise1')
> setTimeout(()=>{
> console.log('setTimeout2')
> },0)
> });
> setTimeout(()=>{
> console.log('setTimeout1')
> Promise.resolve().then(()=>{
> console.log('Promise2')
> })
> },0);
> console.log('start');
>
> // start
> // Promise1
> // setTimeout1
> // Promise2
> // setTimeout2
> ```
- 字符串方法
> 1. indexof
> 2. lastindexof
> 3. slice
> 4. substr
> 5. sibstring
> 6. concat
> 7. slice
> 8. trim
> 9. replace
### 2. CSS基础
- **两栏布局,三栏布局(5种方法)**
> 1. 浮动方式解决(left,right,center)
> 2. 绝对定位解决(left,center,right)
> 3. flex (left,center,right)
> 4. table (left,center,right) 父元素必须有宽度
> 5. grid (left,center,right) 全在父元素上操作
>
> ```css
> display: grid;
> width: 100%;
> grid-template-rows: 100px;
> grid-template-columns: 300px auto 300px;
> ```
>
>
- **清除浮动方法**
> 1. 在需要清除浮动的元素下面加
.clear{clear:both}
> 2. overflow:hidden
> 3. :after 伪元素
>
> ```css
> .clearfix:after{
> content:'020';
> display:block;
> height:0;
> clear:both;
> visibility: hidden; // 不可以用display:none
> }
> ```
>
>
- **flex布局**
- 父元素
> 1. flex-direction: row| row-reverse|column|column-reverse
> 2. flex-wrap: nowrap | wrap | wrap-reverse;
> 3. justify-content: flex-start | flex-end | center | space-between | space-around;
> 4. align-items: flex-start | flex-end | center | baseline | stretch(默认占满整个高度);
> 5. align-content: 定义了多根轴线的对齐方式,如果项目只有一根轴线,那么该属性将不起作用
item:
> 1. **flex-grow: 定义项目的放大比例**:默认值为 0,即如果存在剩余空间,也不放大,1:等分剩余空间
> 2. **flex-shrink: 定义了项目的缩小比例**:默认值: 1,即如果空间不足,该项目将缩小,负值对该属性无效。0:不缩小
> 3. **flex-basis: 定义了在分配多余空间之前,项目占据的主轴空间,浏览器根据这个属性,计算主轴是否有多余空间**:默认值:auto,即项目本来的大小, 这时候 item 的宽高取决于 width 或 height 的值。当 flex-basis 值为 0 % 时,是把该项目视为零尺寸的,故即使声明该尺寸为 140px,也并没有什么用。
> 4. **flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]**
> 5. flex 的默认值是以上三个属性值的组合。假设以上三个属性同样取默认值,则 flex 的默认值是 0 1 auto。
> 6. **当 flex 取值为一个非负数字,则该数字为 flex-grow 值,flex-shrink 取 1,flex-basis 取 0%,如下是等同的:**
>
> ```css
> .item {flex: 1;}
> .item {
> flex-grow: 1;
> flex-shrink: 1;
> flex-basis: 0%;
> }
> ```
>
> 7. 当 flex 取值为 0 时,对应的三个值分别为 0 1 0%
>
> ```css
> .item {flex: 0;}
> .item {
> flex-grow: 0;
> flex-shrink: 1;
> flex-basis: 0%;
> }
> ```
>
> 8. 当 flex 取值为一个长度或百分比,则视为 flex-basis 值,flex-grow 取 1,flex-shrink 取 1,有如下等同情况(注意 0% 是一个百分比而不是一个非负数字)
>
> ```css
> .item-1 {flex: 0%;}
> .item-1 {
> flex-grow: 1;
> flex-shrink: 1;
> flex-basis: 0%;
> }
>
> .item-2 {flex: 24px;}
> .item-2 {
> flex-grow: 1;
> flex-shrink: 1;
> flex-basis: 24px;
> }
> ```
>
>
- **CSS3有哪些新特性?用过什么?**
> 1. border-radius:50% 50% 50% 50%
> 2. box-shadow:x(正值在对象右面,反之),y(正值在对象下面,反之),阴影模糊程度,阴影扩展半径,颜色
> 3. transform
> 4. transition: all 0 ease 0
> 5. flex
- **垂直水平居中的方法**
> 水平居中:
>
> - 行内元素:
> - text-align:center
> - 块级元素:
> - 需要确定宽度
> - marigin:0 auto
> - 有无宽度均可
> - 绝对定位 left:50% +translateX(-50%)移动元素自身的50%
> - display:flex,just-content:center
> - display:table,margin:0 auto
>
> 垂直居中:
>
> - line-height === height 适合纯文字类
> - 子元素margin:auto+TRBL:0,必须有宽度,父元素相对定位,子元素绝对定位
>
> ```css
> position: absolute;
> margin: auto;
> width: 100px;
> left: 0;
> right: 0;
> top: 0;
> bottom: 0;
> height: 100px;
> ```
>
> - 子元素margin:auto,有无宽度均可,父元素display:flex
> - 父元素display:flex,just-content:center,align-items:center
> - 子元素top:50%,left:50%+translate(),有无宽度均可,父元素相对定位,子元素绝对定位
>
> ```css
> div {
> width: 100px;
> height: 100px;
> position: absolute;
> top: 50%;
> left: 50%;
> transform: translate(-50%, -50%);
> background-color: pink;
> }
> 我是box
> ```
- **盒模型**
`box-sizing: content-box`(W3C盒子模型):元素的宽高大小表现为**内容**的大小。宽高仅仅是内容大小,不包括padding,border
`box-sizing: border-box`(IE盒子模型):元素的宽高表现为**内容 + 内边距 + 边框**的大小。宽高即最终宽高
- **px, em, rem 区别是什么?**
> 1. 都是相对长度单位
> 2. px相对于屏幕分辨率
> 3. em相对父级元素
> 4. rem相对于根元素
- **触发bfc条件**
> 1. overflow 值不为 visible 的块元素
> 2. 绝对定位元素(元素的 position 为 absolute 或 fixed)
> 3. 浮动元素(元素的 float 不是 none)
> 4. 行内块元素(元素的 display 为 inline-block)
>
> BFC渲染规则:
>
> - BFC垂直方向边距重叠
> - BFC的区域不会与浮动元素的box重叠
> - BFC是一个独立的容器,外面的元素不会影响里面的元素
>
> 应用场景:
>
> - 防止浮动导致父元素高度塌陷
>
> - 避免外边距重叠(与子元素:display:inline-block,与父元素:overflow:hidden)
- **css选择器排序**
> 1. !important
> 2. 内联样式
> 3. id选择器
> 4. class选择器/伪类
> 5. 元素选择器/伪元素
> 6. 通配符选择器
### 3. 浏览器
- 浏览器缓存
> 浏览器缓存分为强缓存和协商缓存,发送请求前,首先检查强缓存,如果检查??
>
> 通过相应的字段检查强缓存,在HTTP1.0中,字段为expires,代表到xxx时间节点缓存过期,HTTP1.1中字段为Cache-control,代表过了多久时间到期,如果缓存时间超时了,将会进入协商缓存,浏览器在请求头携带缓存的标签去服务器发送请求,缓存标签有两种 **Last-Modified** 和 **ETag**,**Last-Modified**:最后修改时间,在浏览器第一次给服务器发送请求后,服务器会在响应头中加上这个字段。浏览器再次请求时,会在请求头中携带**`If-Modified-Since`字段**,这个字段的值就是服务器传来的最后修改时间,服务器拿到值后,和服务器资源修改时间做对比,如果传来的时间比最后修改时间小,说明该返回新的资源,否则304,告诉浏览器直接用强缓存,**ETag**:`ETag` 是服务器根据当前文件的内容,给文件生成的唯一标识,只要里面的内容有改动,这个值就会变。服务器通过`响应头`把这个值给浏览器。浏览器接收到`ETag`的值,会在下次请求时,将这个值作为**If-None-Match**这个字段的内容,并放到请求头中,然后发给服务器。服务器接收到**If-None-Match**后,会跟服务器上该资源的**ETag**进行比对:如果两者不一样,说明要更新了。返回新的资源,跟常规的HTTP请求响应的流程一样。否则返回304,告诉浏览器直接用缓存。
>
> 缓存位置:
>
> - Service Worker
> - Memory Cache
> - Disk Cache
> - Push Cache
- cookie,localstroge,sessionstorage区别
> 1. 大小:cookie:4kb;localstorage,sessionstorage:5MB
> 2. 存储时间:cookie不设置过期时间(浏览器关闭就消失);sessionstorage浏览器关闭就消失,localstorage需要手动删除
> 3. 性能:cookie:同一域名不同地址请求都携带cookie;localstorage,sessionstorage:只存在客户端,默认不参与与服务端的通信。
- 什么情况算是跨域?如何解决跨域问题?
> 1. 当浏览器向目标 URI 发 Ajax 请求时,只要当前 URL 和目标 URL 不同源(协议,域名,端口),则产生跨域,被称为`跨域请求`。
> 2. proxy配置,Nginx配置
### 4. 网络
- TCP
- 1. 能不能说一说 TCP 和 UDP 的区别?
> 1. **TCP是一个面向连接的、可靠的、基于字节流的传输层协议。**
>
> 2. **UDP是一个面向无连接的传输层协议。**
>
> - **面向连接**。所谓的连接,指的是客户端和服务器的连接,在双方互相通信之前,TCP 需要三次握手建立连接,而 UDP 没有相应建立连接的过程。
> - **可靠性**。TCP 花了非常多的功夫保证连接的可靠,这个可靠性体现在哪些方面呢?一个是有状态,另一个是可控制。
> - TCP 会精准记录哪些数据发送了,哪些数据被对方接收了,哪些没有被接收到,而且保证数据包按序到达,不允许半点差错。这是**有状态**。
> - 当意识到丢包了或者网络环境不佳,TCP 会根据具体情况调整自己的行为,控制自己的发送速度或者重发。这是**可控制**。
>
> 相应的,UDP 就是`无状态`, `不可控`的。
>
> 3. **面向字节流**。UDP 的数据传输是基于数据报的,这是因为仅仅只是继承了 IP 层的特性,而 TCP 为了维护状态,将一个个 IP 包变成了字节流。
- 2. 三次握手
> 每个人都有爱与被爱的能力
>
> 男 to 女:我爱你 --> 男拥有爱的能力
>
> 女 to 男: 我收到了你的爱,我也爱你 --> 女拥有被爱与爱的能力
>
> 男 to 女:我收到了你的爱 --> 两人可以开始甜蜜的恋爱:heart:
>
> #### 为什么不是两次?
>
> 根本原因: 无法确认客户端的接收能力。
>
> 
>
> 1. 从最开始双方都属于CLOSED状态,然后服务器开始监听某个端口,进入了LISTEN状态
> 2. 然后客户端主动发起连接,发送SYN,自己变成了SYN-SENT状态
> 3. 服务端接收到,返回SYN和ACK(对应客户端发来的SYN),自己状态变成了SYN-RECV
> 4. 之后客户端再发送ACK给服务端,自己变成了`ESTABLISHED`状态,服务端收到`ACK`之后,也变成了`ESTABLISHED`状态
>
> 从图中可以看出,SYN 是需要消耗一个序列号的,下次发送对应的 ACK 序列号要加1,为什么呢?只需要记住一个规则:
>
> > 凡是需要对端确认的,一定消耗TCP报文的序列号。
>
> SYN(握手信号) 需要对端的确认, 而 ACK(确定收到数据的信号) 并不需要,因此 SYN 消耗一个序列号而 ACK 不需要。
- 3. 四次挥手
> 男 to 女:我可以挂电话了嘛
>
> 女 to 男:等下,我在敷面膜
>
> 女 to 男:我敷完了,可以挂电话了
>
> 男 to 女: 我舍不得挂,你挂吧
>
> 女:挂断电话,男:等待2MSL(报文最大生存时间)后挂断
>
> **那,照这样说一个 MSL 不就不够了吗,为什么要等待 2 MSL?**
>
> - 1 个 MSL 确保四次挥手中主动关闭方最后的 ACK 报文最终能达到对端
> - 1 个 MSL 确保对端没有收到 ACK 重传的 FIN 报文可以到达
>
> 
>
> 1. 刚开始双方处于`ESTABLISHED`状态
> 2. 客户端要断开了,向服务端发送`FIN`(表示后面没有数据需要发送)报文,客户端变成了`FIN-WAIT1`状态,这时候客户端已经变成了半关闭状态`half-close`,无法发送报文,只能接受
> 3. 服务端接受后向客户端确认,变成了`CLOSE-WAIT`状态
> 4. 客户端收到了服务端的确认,变成了`FIN-WAIT2`状态
> 5. 随后,服务端向客户端发送`FIN`,自己变为`LAST-ACK`状态
> 6. 客户端收到服务端发来的`FIN`后,自己变为`TIME-WAIT`状态,然后发送`ACK`给服务端
> 7. 注意了,这个时候,客户端需要等待足够长的时间,具体来说,是 2 个 `MSL`(`Maximum Segment Lifetime,报文最大生存时间`), 在这段时间内如果客户端没有收到服务端的重发请求,那么表示 ACK 成功到达,挥手结束,否则客户端重发 ACK。
- 为什么建立连接是三次握手,而关闭连接却是四次挥手呢?
因为三次挥手意味着把服务端向客户端发送的Fin和Ack报文合成一起,实际上,当服务端收到客户端的Fin报文后,并不会立即发送Fin报文,会先发送Ack报文确定自己收到信息了,如果合起来发送的话,会等一会才会发送,这也就容易让客户端认为服务端没有收到,导致重传,资源浪费。
- 4. 能不能说一说 TCP 的流量控制?
对于发送端和接收端而言,TCP 需要把发送的数据放到**发送缓存区**, 将接收的数据放到**接收缓存区**。
而流量控制所要做的事情,就是在通过接收缓存区的大小,控制发送端的发送。如果对方的接收缓存区满了,就不能再继续发送了。
- 5. 能不能说说 TCP 的拥塞控制?
- HTTP
> 1. 报文结构:
> - 请求行(GET/home HTTP/1.1)(HTTP/1.1 200 OK)
> - 请求头
> - 空行
> - 请求体
> 2. 常用请求头:
> - User-Agent:客户端相关信息(浏览器相关信息)
> - Authorization:客户端给服务端进行权限认证的信息
> - Cookie:携带的Cookie信息
> - Content-Type:请求体内容类型
> - Host:主机ip地址或域名
> - Cache-Control:缓存机制
> 3. 常见响应头:
> - ETag:资源的匹配信息
> - Server:服务器相关信息
> - Location:令客户端重定向至指定URI
> - Connection:Keep-Alive保持tcp连接不关闭,不会永久保持链接
> 4. 状态码:
> - **1xx**: 表示目前是协议处理的中间状态,还需要后续操作。
> - **2xx**: 表示成功状态。
> - 204:服务器成功处理了请求,没有返回任何内容。
> - 202:服务器已接受请求,但尚未处理。
> - **3xx**: 重定向状态,资源位置发生变动,需要重新请求。
> - 301:永久重定向,浏览器会做缓存优化
> - 302:暂时重定向
> - **4xx**: 请求报文有误。
> - 403:服务器禁止访问
> - 405:请求方法不被服务端允许
> - 406:资源无法满足客户端条件
> - 408:服务器等待了太长时间
> - **5xx**: 服务器端发生错误。
> - 501:客户端请求的功能还不支持
> - 502:服务器自身正常,访问出错,至于为什么错,咱也不知道
> - 503:服务器很忙,**暂时无法响应服务**
- get,post有什么区别
> - 从**缓存**的角度,GET 请求会被浏览器主动缓存下来,留下历史记录,而 POST 默认不会。
> - 从**参数**的角度,GET 一般放在 URL 中,因此不安全,POST 放在请求体中,更适合传输敏感信息。
> - 从**TCP**的角度,GET 请求会把请求报文一次性发出去,而 POST 会分为两个 TCP 数据包,首先发 header 部分,如果服务器响应 100(continue), 然后发 body 部分。(**火狐**浏览器除外,它的 POST 请求只发一个 TCP 包)
- 从输入一个url到浏览器页面展示都经历了哪些过程
> 1. 构建请求
> 2. 查找强缓存
> 3. DNS解析
> 4. 建立TCP连接
> 5. 发送HTTP请求
> 6. 服务器处理请求,客户端响应
- 如何理解 URI?
> **URI**, 全称为(Uniform Resource Identifier), 也就是**统一资源标识符**,它的作用很简单,就是区分互联网上不同的资源。
>
> 但是,它并不是我们常说的`网址`, 网址指的是`URL`, 实际上`URI`包含了`URN`和`URL`两个部分,由于 URL 过于普及,就默认将 URI 视为 URL 了。
>
> 
- TCP的流量控制;✔
- HTTP和HTTPS区别:
> 1. http:// 和 https://
> 2. http是不经安全加密的协议,https是通过算法加密的协议
> 3. http的端口是80,https端口是443
- HTTP特点
> 1. 可靠传输
> 2. 请求-应答模式,一发一答,有来有回
> 3. 无状态
- 简述 HTTP1.0/1.1/2.0 的区别
> 1. HTTP 1.0 被设计用来使用短链接,即每次发送数据都会经过 TCP 的三次握手和四次挥手,效率比较低。
> 2. HTTP 1.0 请求头中的 If-Modified-Since 和 Expires 作为缓存失效的标准。
> 3. HTTP 1.1 中新增加了 E-tag,If-Unmodified-Since, If-Match, If-None-Match 等缓存控制标头来控制缓存失效。
> 4. HTTP 1.1 默认使用长连接,长连接就是只需一次建立就可以传输多次数据,传输完成后,只需要一次切断连接即可。长连接的连接时长可以通过请求头中的 `keep-alive` 来设置
> 5. HTTP2.0:多路复用
> 6. HTTP2.0:首部压缩
- 网络层
> 1. 应用层
> 2. 传输层
> 3. 网络层
> 4. 链路层
> 5. 物理层

### 5. git && webpack
- git版本回退
> 1. git reflog
> 2. git reset --hard HEAD@{id}
- git reset:**适用场景:** 如果想恢复到之前某个提交的版本,且那个版本之后提交的版本我们都不要了,就可以用这种方法。
- git revert:**适用场景:** 如果我们想撤销之前的某一版本,但是又想保留该目标版本后面的版本,记录下这整个版本变动流程,就可以用这种方法。
- 热更新的实现原理
> 热更新指不用刷新整个页面,就可以完整新模块替换旧模块
>
> 热更新的核心就是客户端从服务端拉取更新后的文件,就是WDS与浏览器中间维护了一个webSocket协议,当本地资源发生变化时,WDS会向浏览器推送更新,并带上构建时候的hash值,让客户端与上一次资源进行对比,如果对比出差异就会向WDS发ajax请求,来获取更新内容。
- webpack如何构建优化
> 1. 保证webpack和node的版本为最新
> 2. 优化babel-loader(a.开启缓存,没有改变的ES6代码直接用启用缓存里的 b.明确范围:只打包xxx路径下的)
>
> 
>
>
>
> 1. happyPack多进程打包
> 2. Dllplugin,一些大的文件可以提前打包好,等打包完成之后再去引用他
- 有哪些常见的loader?
> 1. sass-loader:将SCSS/SASS代码转换成CSS
> 2. bable-loader:把 ES6 转换成 ES5
> 3. css-loader:加载 CSS,支持模块化、压缩、文件导入等特性
> 4. image-loader:加载并且压缩图片文件
- 有哪些常见的Plugin?你用过哪些Plugin?
> 1. html-webpack-plugin:简化 HTML 文件创建 (依赖于 html-loader)
- git如何修改commit的信息;
> 1. 修改最近一次的
>
> git commit --amend
>
> 2. 修改之前某一次的
>
> git reflog
>
> git rebase -i HEAD~n
>
> 然后将要修改的那一行改为edit
>
> git commit --amend
>
> git rebase --continue
- git merge和rebase的区别;
> 1. merge和rebase都是用来合并分支的。
>
> 2. merge :保留完整的commit历史
>
> ```js
> git checkout feature
> git merge master
> ```
>
> 3. rebase:本质是变基 变基 变基,不会保留之前的commit历史
>
> ```js
> git checkout feature
> git rebase master
> ```
- pull和fetch区别
> 1. git fetch:相当于是从远程获取最新版本到本地,不会自动merge
> 2. git pull:相当于是从远程获取最新版本并merge到本地
- tag branch
> 1. ##### branch-分支,是由一连串和一系列的commit组成的。
>
> 2. ##### tag-标签,标记某一个时间点的commit。记录历史性时刻
- 分支:
> 1. 创建分支:git branch name
> 2. 切换分支:git checkout name
> 3. 删除分支:git branch -d name
- git 如何解决代码(版本)冲突?
> 1. git status查看状态
> 2. vim进入这个文件,查看变化
> 3. 修改自己想要的数据
> 4. 提交到远程仓库
- webpack打包流程
> 1. 配置初始化(配置文件)
> 2. 实例化Compiler,注册plugin
> 3. 解析文件的路径信息
> 4. runloaders处理源码,解析为ast
> 5. 生成chunk,优化chunk
> 6. 构建资源,生成文件
- webpack打包原理
> 把所有依赖打包成一个bundle.js文件,把代码分割成单元片段并按需加载。
### 6. Vue面试题
- mounted和created区别
> 1. created:在模板渲染成html前调用,不可以操作节点
> 2. mounted:在模板渲染成html后调用,可以操作节点
- .stop和.prevent区别
> stop:阻止事件冒泡
>
> prevent:阻止默认事件(a标签,表单提交)
- 防抖和节流的区别
> ```js
> // 防抖函数
> // 触发高频事件后n秒函数执行
> const debounce = (fn,delay)=>{
> let timeout = null;
> return function(...arg){
> // 清除上一个计时器,频繁触发事件会卡在这里
> if(timeout) clearTimeout(timeout)
> timeout = setTimeout(()=>{
> fn.apply(this,arg)
> },delay)
> }
> }
> ```
>
> 节流
>
> ```js
> function throttle(fn, delay) {
> let isRunning = false
> return function () {
> if (isRunning) return
> isRunning = true
> setTimeout(() => {
> fn.call(thiswqhgfs)
> isRunning = false
> }, delay);
> }
> }
> ```
>
> 举个例子:用户输入框输入文字,如果时间间隔设为1s,
>
> - 防抖:用户多次输入停止后1s执行函数
> - 节流:用户输入过程中,每隔1s执行一次函数
- 父子组件通信有几种方式
> 1. props/$emit
> 2. eventbus
> 3. vuex
> 4. attrs/listeners
> 5. ref
- axios:
> 1. 基于promise的方式封装了浏览器的XMLHttpRequest请求,和服务端node http请求
> 2. 支持 Promise API
> 3. 拦截请求和响应
- ajax:
> 1. JQuery整个项目太大,单纯使用ajax却要引入整个JQuery非常的不合理
> 2. 不符合前后端分离的浪潮
- fetch:
> 1. 更好更方便的写法
> 2. 脱离了XHR,是ES规范里新的实现方式
> 3. 更加贴近于原生
- vue如何创建一个插件
> 1. 创建组件构造器
> 2. 通过new的方式,可以创建组件对象
> 3. 将组件对象挂载到某一个元素
> 4. 全局注册插件
> 5. Vue.use(name)
1. Vue的插件是一个**对象**, 就像`Element`.
2. 插件**对象**必须有`install`字段.
3. `install`字段是一个函数.
4. 初始化插件**对象**需要通过`Vue.use()`
- vue如何自定义指令
> 1. vue.directive
> 2. 第一个参数为指令名称
> 3. bind传递参数
- EventBus原理
> 1. 发布订阅模式
- Vue.prototype.$xxx= xxx;为vue对象添加了一个原型属性
- 做过哪些性能优化
> 1. v-if和v-for不能连用
> 2. 防抖
> 3. 图片懒加载
> 4. SPA 页面采用keep-alive缓存组件
> 5. 尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcher
- **vue-router原理**
> 1. 实现数据监控,页面跳转:路由发生变化(hash有hashChange监听方法,history有popstate),改变浏览器里的地址,再更新视图。history模式中,主要通过pushstate、replaceState实现,它们负责改变浏览器的路由,但是不跳转,这就实现了前端的路由,而popstate是监听方法,处理路由改变后,前端页面的显示问题
>
> 2. ```js
> window.addEventListener('hashChange', function() { // ... });
>
> window.addEventListener('popstate', function() { // ... });
> ```
>
> 3. history.pushState:添加历史记录
>
> 4. history.replaceState:修改当前记录
>
> 5. `popstate`事件,每当同一个文档的浏览历史(即history对象)出现变化时,就会触发`popstate`事件。但是都不会跳转页面,只有用户点击前进后退,或者调用back,forward。go方法时才会跳转。
>
> 6. **区别:**
>
> - url 展示上,hash 模式有“#”,history 模式没有;
> - 刷新页面时,hash 模式可以正常加载到 hash 值对应的页面,而 history 没有处理的话,会出现404;
> - 兼容性,hash 可以支持低版本浏览器和 IE。
- vuex原理
> 1. vuex是专业为vue服务的状态管理模式
> 2. 本质上,vuex是一个没有template的隐藏的vue组件,vuex中的state是响应式,是借助vue中的data是响应式,把state存入vue组件中的data中,vuex中的getters则是借助vue的计算属性computed实现数据实时监听。
- promise原理
> 1. Promise是个构造函数,有参数是执行器函数,有3种状态
> 2. 执行器函数执行两个函数,resolve,reject
> 3. resolve和reject函数会先判断状态是否是pending,如果不是,则直接结束
> 4. 如果是,则修改状态,保存数据
> 5. 如果有待执行callback函数,立即异步执行回调函数
- 首屏优化
> 1. 路由懒加载
> 2. 打包时去掉sourceMap文件
> 3. 开启gzip压缩
> 4. CDN引入
- 
### 7. 算法
- 排序
> 冒泡排序:n方
>
> 选择排序:n方
>
> 堆排序:nlogn
>
> 插入排序:n方,对于快要完成排序的数组n
>
> 快速排序:最差情况n方 /最优nlogn
>
> 快排最好的情况是,每次正好中分,复杂度为O(nlogn)。最差情况,分的是一个元素和其他元素两部分,复杂度为O(n^2),退化成冒泡排序
>
> 优化:**使用左端,右端和中心的中值做为基准。**
- 去重✔
- 一个有序的数组进行查找操作?
- 手写一个快速排序
- 链条反转/数组反转
> 1. 链表分为:单链表,双链表,循环链表
- 快排、插入、堆排的原理,复杂度
- 随机排序
```js
function randomsort(a, b) {
return Math.random()>.5 ? -1 : 1; //通过随机产生0到1的数,然后判断是否大于0.5从而影响排序,产生随机性的效果。
}
var arr = [1, 2, 3, 4, 5];
arr.sort(randomsort);
```
- 斐波那契数列
```js
function fib(n){
var res = [1,1];
if(n == 1){
return 1;
}else if(n == 2){
return 2
}
for(var i=2;i 根左右
>
> 左根右
>
> 左右根
- 队列结构
> 1. 是一种受限的线性结构,从后端添加在前端删除,先进先出,first in first out
- 栈结构
> 1. 是一种受限的线性结构,从栈顶增加和删除,先进后出,first in last out
- 二叉搜索树

- 二叉树:
> 1. 一个二叉树的第i层最大节点数2^(n-1)
> 2. 完美二叉树:除了叶子节点,每一个节点都有两个子节点
> 3. 完全二叉树:最后一层节点,左子树节点必须全有,只允许右子树节点缺失
### 8. HTML
- 常用的meta头;
> - charset属性
>
> ```html
>
>
> ```
>
> - name + content属性
>
> ```html
>
>
>
> ```
>
> - http-equiv+content属性
>
> ```html
>
>
>
>
>
>
> ```
- JS文件摆放位置,async和defer
> 1. 当script中有defer属性时,脚本的加载过程和文档加载是异步发生的,等到文档解析完(DOMContentLoaded事件发生)脚本才开始执行。
> 2. 当script有async属性时,脚本的加载过程和文档加载也是异步发生的。但脚本下载完成后会停止HTML解析,执行脚本,脚本解析完继续HTML解析。
> 3. 当script同时有async和defer属性时,执行效果和async一致。
- HTML5有哪些新特性?用过什么?
> 1. 语义特性,添加`