# 面试 **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. ![image-20200618141006855](C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20200618141006855.png) 并行串行 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循环中的应用 串行 ![image-20200618143201996](C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20200618143201996.png) 并行: ![image-20200618143257764](C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20200618143257764.png) - 事件冒泡和事件捕获以及事件委托 ✔ - 手写深拷贝 > 考虑: > > 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: > > #### 为什么不是两次? > > 根本原因: 无法确认客户端的接收能力。 > > ![image-20200620212550877](C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20200620212550877.png) > > 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 报文可以到达 > > ![image-20200620213948142](C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20200620213948142.png) > > 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 了。 > > ![image-20200621181032168](C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20200621181032168.png) - 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. 物理层 ![image-20200620192415648](C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20200620192415648.png) ### 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路径下的) > > ![image-20200625144319915](C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20200625144319915.png) > > > > 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引入 - ![image-20200703084952815](C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20200703084952815.png) ### 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 - 二叉搜索树 ![image-20200626213759037](C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20200626213759037.png) - 二叉树: > 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. 语义特性,添加`