2020面试题整理

346 阅读46分钟

链接:juejin.cn/post/684490…

1.如何理解html标签语义化?

合理的标签做合理的事情, 能让浏览器更好的读取页面结构, 便于团队开发和维护, 有利于SEO, 常用的语义化标签包括:

<header>页面的头部</header>

<nav>语义:导航条</nav>

<article>语义:文章</article>

<section>语义:部分 区块 </section>

<main>主要区域</main>
    
<aside>语义:定义侧边栏</aside>

<footer>语义:页面的底部</footer>

<datalist>语义:定义下拉列表 和 input搭配使用</datalist>

<fieldset>语义:可将表单内的相关元素分组,打包,legend搭配使用</fieldset>

2.css权重是什么?

设置节点样式的方式有很多种,不同的方式它们的权重并不相同,当它们给一个节点设置同一个样式时,谁的权重高谁就生效。

  • important:无限高
  • 行内样式:权重值为1000
  • id选择器:权重值为100
  • 类、伪类、属性选择器:权重值为10
  • 标签选择器:权重值为1

3.盒模型有几种,它们区别是什么?

content、padding、border、margin

  • 标准盒子模型(box-sizing:content-box)
  • ie怪异盒子模型(box-sizing:border-box)
  • flex弹性伸缩盒模型
  • column多列布局(栅格化布局 基本不用)
  • 注:怪异盒模型的好处,固定到border控制宽高,不用重新计算padding和border

4. 什么是BFC?

块级格式上下文,一句话来说就是让块级元素有块级元素该有的样子,触发BFC可以清除浮动、让margin不重叠。

  • float的值不为none。(left、right)
  • position的值不为static或relative中的任何一个。(absolute、fixed)
  • overflow的值不为visible。(hidden、auto、scroll)
  • display的值为table-cell、table-caption和inline-block之一。

5.px、em、rem、vw/vh的区别?

  • px:物理屏幕上能显示出的最小的一个点。
  • em:如果父级有设置字体大小,1em就是父级的大小,没有1em等于自身默认的字体大小。
  • rem:相对于html标签的字体大小。
  • vw/vh:相对于视口的高度和宽度,1vh等于1/100的视口高度,1vw等于1/100的视口宽度。

6. 不使用border属性画一条1px的线?

<div style='height: 1px; background: #666; overflow: hidden;'></div>

<hr size='1'></hr>

7. 移动端1px问题的解决办法

www.jianshu.com/p/31f890763…

  • 原因:
  • 由于不同的手机有不同的像素密度导致的。如果移动显示屏的分辨率始终是普通屏幕的2倍,1px的边框在devicePixelRatio=2的移动显示屏下会显示成2px,所以在高清瓶下看着1px总是感觉变胖了
  • 1)在ios8+中当devicePixelRatio=2的时候使用0.5px
p{
   border:1px solid #000;
}
@media (-webkit-min-device-pixel-ratio: 2) {
    p{
        border:0.5px solid #000;
    }
}
  • 2)伪元素 + transform 实现
  • 对于老项目伪元素+transform是比较完美的方法了。 原理是把原先元素的 border 去掉,然后利用 :before 或者 :after 重做 border ,并 transform 的 scale 缩小一半,原先的元素相对定位,新做的 border 绝对定位。 单条border样式设置:
.scale-1px{ position: relative; border:none; }
.scale-1px:after{
    content: '';
    position: absolute; bottom: 0;
    background: #000;
    width: 100%; height: 1px;
    -webkit-transform: scaleY(0.5);
    transform: scaleY(0.5);
     -webkit-transform-origin: 0 0;
    transform-origin: 0 0; 
}
  • 优点:所有场景都能满足,支持圆角(伪元素和本体都需要加border-radius)
  • 缺点:对于已经使用伪元素的元素(例如clearfix),可能需要多层嵌套
  • 3)viewport + rem 实现 这种兼容方案相对比较完美,适合新的项目,老的项目修改成本过大。 在devicePixelRatio = 2 时,输出viewport: 在devicePixelRatio = 3 时,输出viewport:
  • 优点:所有场景都能满足,一套代码,可以兼容基本所有布局
  • 缺点:老项目修改代价过大,只适用于新项目
  • 4)使用box-shadow模拟边框 利用css 对阴影处理的方式实现0.5px的效果 样式设置:
.box-shadow-1px {
box-shadow: inset 0px -1px 1px -1px #c8c7cc;
}
  • 优点:代码量少,可以满足所有场景
  • 缺点:边框有阴影,颜色变浅

8. 定位的方式有哪几种,它们的区别是什么?

  • relative:相较于自身定位,设置的位置相对于自己进行位移。不脱离文档流。
  • absolute:相较于最近有定位的父节点定位,设置的位置相较于父节点。会脱离文档流,导致父节点高度塌陷。
  • fixed:相较于当前窗口进行定位,设置的位置相较于窗口。脱离文档流。
  • static 默认值。没有定位,元素出现在正常的流中
  • sticky 生成粘性定位的元素,容器的位置根据正常文档流计算得出
  • 粘性定位 blog.csdn.net/qq_35585701…

9. 垂直水平居中的实现方式有哪些?

  • 父级设置text-align: center和line-height等同高度。
  • 子节点绝对定位,设置position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);
  • 子节点绝对定位,需要设置宽度和高度。设置position: absolute;top:0;left:0;right:0;bottom:0;margin:auto;
  • 父级设置display: table,子节点设置display:table-cell;text-align:center;vertical-align:middle;
  • 父级设置display: flex;justify-content:center;align-items:center;
  • 父节点设置display: grid;,子节点设置:align-self:center;justify-self: center;

10.你知道的左右宽度固定,中间自适应的三栏布局方案有哪些?

浮动:
.parent {overflow: hidden;}
.left {float: left; width: 100px;}
.right: {float: right; width: 100px;}
<div class='parent'>
  <div class='left'></div>
  <div class='right'></div>
  <div class='center'></div>
</div>

定位1:
.parent {position: relative};
.left {position: absolute; left: 0; width: 100px};
.right {position: absolute; right: 0; width: 100px};
.center {postion: absolute; left: 100px; right: 100px};

定位2:
.parent {position: relative};
.left {position: absolute; left: 0; width: 100px};
.right {position: absolute; right: 0; top: 0; width: 100px};
.center {margin: 0 100px 0 100px};

表格:
.parent {dispaly: table; width: 100%;}
.left {display: table-cell; width: 100px;}
.center {display: table-cell;}
.right {display: table-cell; width: 100px;}

弹性:
.parent {display: flex;}
.left {width: 100px;}
.center {flex: 1;}
.right {width: 100px;}

网格:
.parent {
  display: grid; 
  width: 100%; 
  grid-template-rows: 100px; 
  grid-template-columns: 100px auto 100px;
}

11.实现三个圆形的水平自适应布局?

难点在于高度的自适应
.parent {
  display: table;
  width: 100%;
}
.child {
  display: table-cell;
  padding-top: 33.33%;
  background: red;
  border-radius: 50%;
}

.parent {
  overflow: hidden;
}
.child {
  float: left;
  width: 33.33%;
  padding-top: 33.33%;
  border-radius: 50%;
  background: red;
}

.parent {
  display: flex;
}
.child {
  flex: 1;
  padding-top: 33.33%;
  border-radius: 50%;
  background: red;
}

12.介绍下flex布局?

主轴方向:水平排列(默认) | 水平反向排列 | 垂直排列 | 垂直反向排列
flex-direction: row | row-reverse | column | column-reverse;

换行:不换行(默认) | 换行 | 反向换行(第一行在最后面)
flex-wrap: nowrap | wrap | wrap-reverse;

flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap
flex-flow: <flex-direction> || <flex-wrap>;

主轴对齐方式:起点对齐(默认) | 终点对齐 | 居中对齐 | 两端对齐 | 分散对齐
justify-content: flex-start | flex-end | center | space-between | space-around;

交叉轴对齐方式:拉伸对齐(默认) | 起点对齐 | 终点对齐 | 居中对齐 | 第一行文字的基线对齐
align-items: stretch | flex-start | flex-end | center | baseline;

多根轴线对齐方式:拉伸对齐(默认) | 起点对齐 | 终点对齐 | 居中对齐 | 两端对齐 | 分散对齐
align-content: stretch | flex-start | flex-end | center | space-between | space-around;

13.JavaScript的变量有哪些类型?

分为两种:基础类型和引用类型。

  • 基础类型:boolean、null、undefined、number、string、symbol(创建唯一值的)。
  • 引用类型:Array、Object、Function。

14.基础类型和引用的区别?

  • 它们在内存中存储的方式不同。基础类型存储的是值,而引用类型存储的是指向内存中某个空间的指针;
  • 基础类型赋值就是把值赋给另外一个变量,而引用类型的赋值是赋值的原来变量的指针,所以当引用类型发生改变时,只要是指向同一个指针的变量的都会发生改变。

15.函数参数是对象时会发生什么问题?

  • 函数参数是对象时,相当于是将对象的指针传递给了函数,如果在函数的内部改变了对象的值,外面对象的值也会发生改变,数组也是如此。

16.typeof和instanceof判断变量类型的区别?

  • typeof对于基础类型除了null以外都可以显示正确的类型,对于数组和对象都会显示object,对于函数会显示function。
  • instanceof主要是用来判断引用类型,它的原理是根据原型链来查找。

17.有没有更好的判断变量类型的方法?

  • 可以使用Object.prototype.toString.call(var),可以更加准确的判断某个变量的类型。

18.类数组转为数组的方式有哪些?

[].slice.call(arguments)
Array.from(arguments)
[...arguments]

19.如何判断一个变量是否是数组?

arr instanceof Array
Array.prototype.isPrototypeOf(arr)
Array.isArray(arr)
Object.prototype.toString.call(arr) === '[object Array]'
arr.constructor === Array

20.将多维数组扁平化?

function flatten(arr) {
  return [].concat(...arr.map(v => {
    return Array.isArray(v) ? flatten(v) : v;
  }))
}

function flatten(arr) {
  return arr.reduce((pre, cur) => {
    return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);
  }, [])
}

function flatten(arr) {
  return arr.flat(Infinity);
}

function flatten(arr) {  // 纯数字
  return arr.toString().split(',').map(Number);
}

function flatten(arr) {
  const ret = [];
  while (arr.length) {
    const item = arr.shift();
    if (Array.isArray(item)) {
      arr.unshift(...item);
    } else {
      ret.push(item);
    }
  }
  return ret;
}

21.数组去重?

  • 一、利用ES6 Set去重(ES6中最常用)
function unique (arr) {
  return Array.from(new Set(arr))
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
 //[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {}]
这种去重的方法代码最少,但是无法去掉“{}”空对象

  • 二、利用for嵌套for,然后splice去重(ES5中最常用)
function unique(arr){            
        for(var i=0; i<arr.length; i++){
            for(var j=i+1; j<arr.length; j++){
                if(arr[i]==arr[j]){         //第一个等同于第二个,splice方法删除第二个
                    arr.splice(j,1);
                    j--;
                }
            }
        }
return arr;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
    console.log(unique(arr))
    //[1, "true", 15, false, undefined, NaN, NaN, "NaN", "a", {…}, {…}]     //NaN和{}没有去重,两个null直接消失了
注:双层循环,外层循环元素,内层循环时比较值。值相同时,则删去这个值。
  • 三、利用indexOf去重:新建一个空的结果数组,for
 循环原数组,判断结果数组是否存在当前元素,如果有相同的值则跳过,不相同则push进数组。
function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    var array = [];
    for (var i = 0; i < arr.length; i++) {
        if (array .indexOf(arr[i]) === -1) {
            array .push(arr[i])
        }
    }
    return array;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
   // [1, "true", true, 15, false, undefined, null, NaN, NaN, "NaN", 0, "a", {…}, {…}]  //NaN、{}没有去重
  • 四、利用sort(),根据排序后的结果进行遍历及相邻元素比对。
function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return;
    }
    arr = arr.sort()
    var arrry= [arr[0]];
    for (var i = 1; i < arr.length; i++) {
        if (arr[i] !== arr[i-1]) {
            arrry.push(arr[i]);
        }
    }
    return arrry;
}
     var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
        console.log(unique(arr))
// [0, 1, 15, "NaN", NaN, NaN, {…}, {…}, "a", false, null, true, "true", undefined]      //NaN、{}没有去重
  • 五、利用includes()
function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    var array =[];
    for(var i = 0; i < arr.length; i++) {
            if( !array.includes( arr[i]) ) {//includes 检测数组是否有某个值
                    array.push(arr[i]);
              }
    }
    return array
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
    console.log(unique(arr))
    //[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}]     //{}没有去重
  • 六、利用reduce+includes
function unique(arr){
    return arr.reduce((prev,cur) => prev.includes(cur) ? prev : [...prev,cur],[]);
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr));
// [1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}]
  • 七、利用hasOwnProperty:判断是否存在对象属性
function unique(arr) {
    var obj = {};
    return arr.filter(function(item, index, arr){
        return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
    })
}
    var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
        console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}]   //所有的都去重了
  • 八、利用filter
function unique(arr) {
  return arr.filter(function(item, index, arr) {
    //当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
    return arr.indexOf(item, 0) === index;
  });
}
    var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
        console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, "NaN", 0, "a", {…}, {…}]
  • 九、利用递归去重
function unique(arr) {
        var array= arr;
        var len = array.length;

    array.sort(function(a,b){   //排序后更加方便去重
        return a - b;
    })

    function loop(index){
        if(index >= 1){
            if(array[index] === array[index-1]){
                array.splice(index,1);
            }
            loop(index - 1);    //递归loop,然后数组去重
        }
    }
    loop(len-1);
    return array;
}
 var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "a", "true", true, 15, false, 1, {…}, null, NaN, NaN, "NaN", 0, "a", {…}, undefined]
  • 十、利用Map数据结构去重:创建一个空Map数据结构,遍历需要去重的数组,把数组的每一个元素作为key存到Map中。由于Map中不会出现相同的key值,所以最终得到的就是去重后的结果。
function arrayNonRepeatfy(arr) {
  let map = new Map();
  let array = new Array();  // 数组用于返回结果
  for (let i = 0; i < arr.length; i++) {
    if(map .has(arr[i])) {  // 如果有该key值
      map .set(arr[i], true); 
    } else { 
      map .set(arr[i], false);   // 如果没有该key值
      array .push(arr[i]);
    }
  } 
  return array ;
}
 var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
    console.log(unique(arr))
//[1, "a", "true", true, 15, false, 1, {…}, null, NaN, NaN, "NaN", 0, "a", {…}, undefined]

22.字符串的test、match、search它们之间的区别?

`test`是检测字符串是否匹配某个正则,返回布尔值;
/[a-z]/.test(1);  // false

`match`是返回检测字符匹配正则的数组结果集合,没有返回`null`;
'1AbC2d'.match(/[a-z]/ig);  // ['A', 'b', 'C', 'd']

`search`是返回正则匹配到的下标,没有返回`-1`。
'1AbC2d'.search(/[a-z]/);  // 2

23.字符串的slice、substring、substr它们之间的区别?

`slice`是返回字符串开始至结束下标减去开始下标个数的新字符串,下标是负数为倒数;
'abcdefg'.slice(2,3);  // c  // 3 - 2
'abcdefg'.slice(3,2);  // ''  // 2 - 3
'abcdefg'.slice(-2,-1);  // f  // -1 - -2

`substring`和`slice`正常截取字符串时相同,负数为0,且下标值小的为开始下标;
'abcdefg'.substring(2,3);  //c  // 3 - 2
'abcdefg'.substring(3,2);  // c  // 3 - 2 
'abcdefg'.substring(3,-3);  // abc  // 3 - 0

`substr`返回开始下标开始加第二个参数(不能为负数)个数的新字符串。
'abcdefg'.substr(2, 3);  // cde
'abcdefg'.substr(3, 2);  // de
'abcdefg'.substr(-3, 2); // ef

24.Number('123')和new Number('123')有什么区别?

Number('123')是一个转换函数,会尝试把参数转为整数类型;而new Number('123')则不同,这是一个构造函数,它的结果是实例化出来一个对象。 同样的情况也适用用String和new String;Boolean和new Boolean的情况。

typeof Number('123') // number
typeof new Number('123') // object

25.==和===的区别?

===会判断两边变量的类型和值是否全部相等,==会存在变量类型转换的问题,所以并不推荐使用,只用一种情况会被使用,var == null是var === undefined || var === null的简写,其余情况一律使用===。

26.是否===就完全靠谱?

  • 也是不一定的,例如0 === -0就为true,NaN === NaN为false,判断两个变量是否完全相等可以使用ES6新增的API,Object.is(0, -0),Object.is(NaN, NaN)就可以准确区分。

27.在类型转换中哪些值会被转为true?

  • 除了undefined、null、false、NaN、''、0、-0以外的值都会被转为true,包括所有引用类型,即使是空的。

28.什么是基本包装类型?

  • 基本类型:Undefined,Null,Boolean,Number,String
  • 基本包装类型:Boolean,Number,String
  • 在基本类型中,有三个比较特殊的存在就是:String Number Boolean,这三个基本类型都有自己对应的包装对象。包装对象,其实就是对象,有相应的属性和方法。调用方法的过程,是在后台偷偷发生的。
//我们平常写程序的过程:
var str = 'hello'; //string 基本类型
var s2 = str.charAt(0); //在执行到这一句的时候 后台会自动完成以下动作 :
( 
 var _str = new String('hello'); // 1 找到对应的包装对象类型,然后通过包装对象创建出一个和基本类型值相同的对象
 var s2 = _str.chaAt(0); // 2 然后这个对象就可以调用包装对象下的方法,并且返回结给s2.
 _str = null;  //    3 之后这个临时创建的对象就被销毁了, str =null; 
 ) 
alert(s2);//h 
alert(str);//hello

29.toString()和valueOf的区别?

  • null和undefined没有以上两个方法。
  • toString:值类型时返回自身的字符串形式;当是引用类型时,无论是一维或多维数组,将他们拍平成一个字符串,里面的null和undefined转为空字符串'',对象转为[object Object],函数的原样返回字符串形式。
  • valueOf无论是值类型还是引用类型,大部分情况下都是原样返回,当是Date类型时,返回时间戳。
  • 在进行字符串强转的时候,toString会优先于valueOf;在进行数值运算时,valueOf会优先于toString。
  • 当执行toString的变量是一个整数类型时,支持传参,表示需要转为多少进制的字符串。

30.谈谈对this的理解?

调用的上下文环境包括全局和局部。

  • 全局环境就是在script标签里面,这里的this始终指向的是window对象。
<script>
    console.log(<strong>this</strong>);//指向window对象
</script>
  • 局部环境
  • 1)在全局作用域下直接调用函数,this指向window
function func(){
  console.log(this) ;//this指向的还是window对象
}
func()
  • 2)对象函数调用,哪个对象调用就指向哪个对象
<input type="button"id="btnOK" value="OK">
<script>
varbtnOK=document.getElementById("btnOK");
btnOK.οnclick=function(){
console.log(this);//this指向的是btnOK对象
}
</script>
  • 3)使用 new 实例化对象,在构造函数中的this指向实例化对象。
var Show=function(){
    this.myName="Mr.Cao";   //这里的this指向的是obj对象
}
var obj=new Show();
4)使用call或apply改变this的指向
var Go=function(){
     this.address="深圳";
}
var Show=function(){
     console.log(this.address);//输出 深圳
}
var go=new Go();
Show.call(go);//改变Show方法的this指向go对象

加分项

1. 用于区分全局变量和局部变量,需要使用this
var age=20;
function show(age){
   this.age=age;
}
2.返回函数当前的对象
live: function( types, data, fn ) {
        jQuery( this.context ).on( types, this.selector, data, fn );
        return this;
    }
3.将当前的对象传递到下一个函数
each: function( callback, args ) {
        return jQuery.each( this, callback, args );
    }

31.改变当前调用this的方式?

  • call:会立即执行调用call方法的函数,不过是以第一个参数为this的情况下调用,方法内可以传递不等的参数,作为调用call方法的参数。
  • apply:运行方式和call是一致的,只是接受的参数不同,不能是不定参数,得是一个数组。
  • bind:会改变当前的this,接受不定参数,不过不会马上执行调用bind方法的函数,而是返回一个函数作为结果,执行后才是调用函数的结果

32.谈谈对闭包的理解?

闭包的实质是因为函数嵌套而形成的作用域链 比如说:函数 A 内部有一个函数 B,函数 B 可以访问到函数 A 中的变量,那么函数 B 就是闭包

  • 用途:为了设计私有的方法和变量(全局访问局部的方式)
  • 优点:访问局部变量,防止全局变量的污染
  • 缺点:占据空间(一直被引用,未能释放空间),所以不能滥用闭包
  • 解决方法:在退出函数之前,将不使用的局部变量全部删除

33.谈谈对原型以及原型链的理解?

  • 原型是一个对象 其他的对象可以通过原型实现属性继承 但是除了 prototype
  • __proto__原型是每个对象(数组,对象,函数)都有的属性,而prototype是函数才有的属性
  • 一般情况下,对象的原型 等于其 '构造器的原型' 即: a. proto === a.constructor.prototype(因为构造器是一个函数,所以构造器的原型为constructor.prototype),除了Object.create方式创建的对象。
__proto__:

        __proto__的指向取决于对象创建时的实现方式

    __proto__ 三种指向:
    1、字面量方式
    var a = {};
    console.log(a.__proto__)//{}
    console.log(a.__proto__ === a.constructor.prototype)

    2、构造器方式
    var A = function () {};
    var b = new A() //{}
    console.log(b.__proto__)//{}
    console.log(b.__proto__ === b.constructor.prototype)
    console.log(b.__proto__ === A.prototype)

    3、Object.create 方式创建
    var  c ={} ;
    var  d = Object.create(c) //{}
    console.log(d.__proto__)//{} === c
    console.log(d.__proto__ === d.constructor.prototype)//false
    console.log(d.__proto__ === c)//true

  • 由于__proto__ 是任何对象都有的属性,而js万物皆对象,所以会形成一条由__proto__ 连起来的链条,递归访问__proto__必须最终到头,并且值为null,当js引擎查找对象的属性时 :先查找对象本身是否有该属性;如果不存在的话,会在原型链上查找,但是不会查找自身的prototype。

34.原型继承的方式有哪些?

  • 原型链继承
  • 直接继承
  • 利用空对象作为中介实现继承
  • 拷贝继承

35.什么是垃圾回收机制?

在程序执行的过程中,解释器会为创建出来的变量分配内存来存储这些变量的实体,执行环境会负责管理代码执行过程中使用到的内存,而何时划出新的内存以及何时把占用的内存释放出来的这样一套内存自动管理机制就是垃圾回收机制。这种周期性的回收策略主要有两种。

  • 标记清除:当变量进入环境时,就将这个变量标记为'进入环境',而当这个变量离开环境时,则将其标记为'离开环境'。垃圾收集器会给内存中的每个变量都做上标记,然后它会去掉环境中的变量以及被环境中变量引用的变量的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,最后垃圾收集器完成内存清除工作。
  • 引用计数:追踪记录每个值被引用的次数,当声明了一个变量并将一个引用类型赋给该变量时,这个变量的引用次数就是1。相反如果包含这个值引用的变量又取得了另外一个值,则这个值的引用次数减1。当为0时,这说明没有办法再访问这个值了,因此垃圾收集器下次运行时,就会释放该值占用的内存。

36.如何解决引用类型变量共享的问题?

  • 可以对引用类型进行深拷贝解决,最简单暴力的深拷贝是JSON.parse(JSON.stringify(obj)),不过也会存在诸多问题,更加完善的深拷贝需要手写递归方法对不同参数分别处理,参考深拷贝的终极探索(90%的人都不知道)。

37.请手写一下map、call、new、instanceof、Events、深拷贝、节流、Promise等?

  • 夯实JavaScript功底,前端要会的手写方法

38.var、let、const的区别 ?

  • var存在变量提升,同一个作用域下可以重复定义同一个名称,后面的覆盖前面的,属于函数作用域。
  • let与const都不存在变量提升,同一个作用域下不可以重复定义同一个名称,都有着严格的作用域(块级作用域)。
  • const只能声明一个只读常量,一旦声明,常量的值就不能改变(不针对于引用类型),一定要初始化,不能只声明不赋值。

39.Set、WeakSet的区别?

  • Set类型内存储的是不会重复的值,建议存储基础类型的值,因为引用类型的指针都不同。
  • WeakSet只能存储对象参数,否则会报错,而且是存储的引用类型的弱引用。
  • WeakSet不可被迭代,不支持forEach、for-of、keys、values方法,没有size属性。

40.Map、WeakMap的区别?

  • map对key强引用,当weakmap引用了一个key的时候,(内存堆空间的)实际key内容不会被垃圾回收掉。 (有内存泄漏风险)
  • weakmap对key弱引用,实际的key可能在某次垃圾回收操作时被清除掉,导致weakmap中的这对key-value也会消失掉。

41.箭头函数和普通函数的区别?

  • 1.箭头函数是匿名函数,不能作为构造函数,不能使用new
  • 2.箭头函数不绑定arguments,取而代之用rest参数...解决
  • 3.箭头函数不绑定this,会捕获其所在的上下文的this值,作为自己的this值
  • 4.箭头函数通过call()或apply()方法调用一个函数时,只传入了一个参数,对this并没有影响。
  • 5.箭头函数没有原型属性
箭头函数和普通函数
箭头函数:
let fun = () => {
    console.log('lalalala');
}
普通函数:
function fun() {
    console.log('lalla');
}
箭头函数相当于匿名函数,并且简化了函数定义。
箭头函数有两种格式,一种只包含一个表达式,连{ ... }和return都省略掉了。
还有一种可以包含多条语句,这时候就不能省略{ ... }和return

42.请实现plus(1)(2)(3)(4)等于8?

方法1:
function plus(n) {
  debugger
  let sum = n;
  const _plus = function (n) {
    sum += n;
    return _plus;
  };
  _plus.toString = function () {
    return sum;
  };
  return _plus;
}

方法2:
function multi() {
  const args = [].slice.call(arguments);
  const fn = function () {
    const newArgs = args.concat([].slice.call(arguments));
    return multi.apply(this, newArgs);
  }
  fn.toString = function () {
    return args.reduce(function (a, b) {
      return a + b;
    })
  }
  return fn;
}

43.谈谈对class的理解 ?

  • JavaScript没有真正的类,一直也是通过函数加原型的形式来模拟,class也不例外,只是语法糖,本质还是函数。需要先声明再使用,内部的方法不会被遍历,且没有函数的prototype属性。不过相较ES6之前无论是定义还是继承都好理解了很多。继承主要是使用extends和super关键字,本质类似于ES5的寄生组合继承:
class Parent {
  constructor(name) {
    this.name = name;
  }
}
class Child extends Parent {
  constructor(name, age) {
    super(name);  // 相当于Parent.call(this, name)
    this.age = age;
  }
}

44.谈谈对Promise的理解 ?

  • Promise是解决异步编程的一种解决方案
  • Promise可以以一种非常优雅的方式来解决网络请求的回调地狱问题(回调中又包含回调)
  • 一般情况下是有异步操作时,使用Promise对这个异步操作进行封装
  • new=>构造函数(1.保存了一些信息状态;2.执行传入的函数)。在执行传入的回调函数时会传入两个参数,resolve和reject(它们本身又是函数)
  • Promise的三种中状态:
  • pending:等待状态,比如正在进行网络请求,或者定时器没有到时间
  • fulfill:满足状态,当我们主动回调了resolve时,就处于该状态,并且会回调.then()
  • reject:拒绝状态,当我们主动回调了reject时,就处于该状态,并且会回调.catch()
  • Promise的all方法使用:用于某个需求需要发送两次请求
  • Promise的race静态方法: 1.race方法接收一个数组, 2.如果数组中有多个Promise对象, 谁先返回状态就听谁的, 后返回的会被抛弃 3.如果数组中不是Promise对象, 那么会直接执行race方法

45.谈谈对ES-Module的理解 ?

  • ES-Module是ES6原生支持模块化方案,通过import来引入模块,通过export default或export来导出模块。

46.谈谈对Proxy的理解 ?

  • 和Object.defineProperty有些类似,它的作用是用来自定义对象中操作。Proxy的构造函数接受两个参数,第一个参数是需要代理的对象,第二个参数是一个对象,里面会定义get和set方法,当代理对象中的某个值被访问或重新赋值就会触发相应的get和set方法。
  • vue3.0就抛弃了Object.defineProperty而拥抱了Proxy,它的优点是只需要代理一次,对象内的值发生了改变就会被感知到,不再需要像以前为对象的每个值进行数据劫持;而且以前对象的新增,数组的下标设置0清空等情况都可以被感知到,在响应式里也不在需要为数组和对象收集两次依赖,相信会大大提升性能。

47.谈谈对Generator的理解?

  • 是JavaScript方便创建迭代器的新语法,在方法名前面添加*号,表示这个方法是一个生成器函数,在函数内部配合yield关键字指定next()方法返回值及顺序。 yield类似与在函数内部打上了断点,yield就是每一处的debugger,执行next()方法后进入下一个断点。 不能使用箭头函数来创建生成器。

48.谈谈对async及await的理解 ?

  • 是Genneator的语法糖形式,解决的问题是以同步的形式写异步代码,让代码流程能很好的表示执行流程。在函数的前面加上async表明是一个异步函数,函数的内部需要配合await关键字使用,每一个await关键字相当于是yield,会暂停函数的执行,直到异步函数执行完毕后内部会自动执行next()方法,执行之后的代码,函数的返回结果是一个Promise对象。因为是以同步的形式书写异步代码,所以错误捕获是使用try/catch的形式。

49.谈谈对Event-Loop的理解 ?

  • JavaScript的执行机制简单来说就先执行同步代码,然后执行异步代码,而异步的代码里面又分为宏任务代码和微任务代码,先执行微任务,然后执行宏任务。首先会将所有JavaScript作为一个宏任务执行,遇到同步的代码就执行,然后开始分配任务,遇到宏任务就将它的回调分配到宏任务的队列里,遇到微任务的回调就分配到微任务的队列里,然后开始执行所有的微任务。执行微任务的过程还是遵循先同步然后分配异步任务的顺序,微任务执行完毕之后,一次Event-Loop的Tick就算完成了。接着挨个去执行分配好的宏任务,在每个宏任务里又先同步后分配异步任务,完成下一次Tick,循环往复直到所有的任务全部执行完成。
  • 微任务包括:process.nextTick ,promise ,MutationObserver。
  • 宏任务包括:script , setTimeout ,setInterval ,setImmediate ,I/O ,UI rendering。

50.对浏览器或元素的各种距离参数你知道哪些?

  • document.documentElement.clientHeight:当前窗口内容区 + 内边距的高度
  • window.innerHeight: 当前窗口内容区 + 内边距 + 边框 + 滚动条高度
  • window.outerHeight:整个浏览器的高度(包括工具栏)
  • clientHeight: 当前元素内容区 + 内边距的高度
  • clientTop: 当前元素上边框的宽度
  • offsetHeight: 当前元素内容区 + 内边距 + 边框 + 滚动条的高度
  • offsetTop: 当前元素的边框距离父元素上外边距的距离
  • scrollHeight: 当前内部可以滚动区域的高度,如果不能滚动则为自己内容区 + 内边距的高度
  • scrollTop: 当前元素滚动离顶部的距离

51.怎么确定当前浏览器的类型?

  • 通过navigator.userAgent获取浏览器信息,根据里面的关键字来确定。

52.什么是简单请求和复杂请求?

简单请求:

  • 请求方法仅限get、head、post。
  • Content-type仅限text/plain、multipart/form-data、application/x-www-form-urlencoded。

复杂请求:

  • 不符合以上条件者就为复杂请求,首先会发起一个option方法的预检请求,来知道服务端是否允许跨域请求。
  • 有一个坑就是服务端设置了CORS,但当客户端发其复杂请求时会验证Authorization字段,但是客户端并没有,所以需要将option方法过滤掉。

53.从输入域名到页面显示都经历了什么?

  • 首先将域名DNS解析为对应的IP地址,然后通过Socket发送数据,经过tcp协议的三次握手,向该地址发起HTTP请求,服务器处理,浏览器收到HTTP响应的数据,关闭tcp连接,开始渲染。

54.谈谈浏览器的渲染机制?

  • 书写的JavaScript、Css、Html在网络传输中都是0和1的字节流,所以浏览器首先会把接受到的这些字节流转为字符串。然后首先将html字节流解析为字符串,对字符串进行标记化,确定标签名以及里面的内容,然后生成对应的node节点,根据节点的结构关系生成DOM树。然后开始解析css,和解析html类似,css一般有嵌套或继承的情况,浏览器会从里到外的递归来确定每个节点的样式是什么,从而生成一颗CSSOM树。最后是将这两颗树结合起来生成一颗渲染树,浏览器根据渲染树进行布局,调用GPU绘制生成页面显示在屏幕上。

55.什么是重绘和回流?

  • 重绘是节点的外观发生改变而不改变布局时,如改变了color这个行为;回流是指改变布局或几何属性发生改变时引起的行为,如添加移除Dom,改变尺寸。它们频繁的触发会影响页面性能。
  • 回流一定触发重绘,而重绘不一定引起回流。回流的成本比重绘高很多,而且子节点的回流,可能引起父节点一系列的回流。

56.如何减少重绘和回流?

  • 使用transform替代位移,使用translate3d开启GPU加速
  • 尽量使用visibility替代display:none
  • 不要使用tanle布局
  • 不要在循环里读取节点的属性值
  • 动画速度越快,回流次数越少

57.什么是事件流/模型?

当某一个事件被触发时,分为三个阶段:

  • 1.事件通过捕获从window => document => body => 目标元素
  • 2.事件到达注册的目标上
  • 3.目标元素通过冒泡返回到window,沿途触发相同类型的事件

58.什么是事件代理?

  • 利用事件流的冒泡特性,将子节点的事件绑定在父节点上,然后在回调里面使用事件对象进行区分,优点是节省内存且不需要给子节点销毁事件。

59.函数防抖和节流的区别?

  • 防抖:指触发事件后在n秒内函数只执行一次,若在n秒内再次触发则重新计算。如:点击按钮,2秒后调用函数,但在1.5秒时又点击了按钮,则会重新计时2秒。(案例:拍照,下拉触底加载下一页)
  • 节流:指连续发生的事件在n秒内只会执行一次(案例:及时查询)

60.什么是事件对象?

  • 这个对象里面存放着触发事件的状态,如触发事件的当前元素,键盘事件是哪个按键触发的,滚动事件的位置等等。

61.什么是跨域?

  • 也就浏览器的同源策略,出于安全的考虑,只要是协议、域名、端口有一个不同就算是跨域,ajax请求就会失败。浏览器有同源策略主要是为了防止CSRF攻击,防止利用户的登录状态发起恶意请求。

62.你知道的解决跨域的方式有几种?

  1. Jsonp
  • 只能get,不安全、有缓存、大小限制
  1. iframe
  • window.name
  • document.domin
  • location.hash
  • post message
  1. CORS-服务端配置
  2. http Proxy
  • webpack添加proxy
  1. nginx反向代理
  • 在build之后proxy不可以用,要服务器端nginx反向代理

63.cookie和session分别是什么?

  • cookie是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器发起请求时被携带并发送到服务器,它通常是告知服务端两个请求是否来自同一浏览器,保持用户的登录状态。

  • session代表着服务器在和客户端一次会话的过程,存储着用户会话所需的属性及配置信息,当用户在不同页面之间跳转时,使整个用户会话一直存在。

  • 简言之,session是服务端存储,服务器设置session,服务器返回给客户端的信息,在响应头中带着set-cookie='contect.sid',客户端会把信息种植到本地的cookie中httponly,客户端再次向服务端发送请求的时候回默认在请求头中,cookie把contect.sid传递给服务器

64.cookie和session有什么不同?

  • 作用范围不同:cookie存储在客户端,session保存在服务器端。
  • 存取的方式不同:cookie只能保存ASCⅡ,session可以存取任意数据类型。
  • 有效期不同:cookie可设置长时间保持,session一般失效时间较短,或客户端关闭就会失效。
  • 存储大小不同:单个cookie保存的数据不能超过4k,session可存储数据远高于cookie。

65.为什么需要cookie和session?

  • 让服务器知道根它打交道的用户是谁以及用户的状态,浏览器发起第一次请求后服务端会返回一个sessionID存储到cookie中,当再次发起请求时服务端根据携带的cookie里的sessionID来查找对应的session信息,没有找到就说明没登录或登录失效,找到说明已经登录,可以进行之后的操作。

66.如果浏览器禁止了cookie怎么办?

每次请求都携带一个SessionID的参数;或者使用token令牌,登录后服务端生成一个Token返回给客户端,以后客户端携带token进行数据请求。

67.使用cookie有哪些注意点?

  • 不建议作为存储方式使用。首先会随请求携带,影响请求的性能,其次存储空间也太小,最后一些属性的使用也需要注意。value:如果作用于登录状态,需要加密。http-only:不能通过JavaScript访问到cookie,防止XSS攻击。same-site:不能在跨域请求中携带cookie,防止CSRF攻击。

68.前后端实现登录的方式有哪些?

  • cookie + session:前端登录后,后端会种一个httpOnly的cookie在前端,里面就有这个用户对应的sessionId,以后每一次前端发起请求会携带上这个cookie,后端从里面解析到sessionId后找到对应的session信息,就知道是谁再操作了。缺点是后端需要空间存储session,用户多了,服务器多了都不方便,这种方式基本属于淘汰边缘。
  • jwt + token:前端登录后,后端会返回一个包括用户信息加密的token字符串(可能还有过期时间,手机端有设备唯一码等信息),客户端自己保存了,将这个token设置到header里的Authorization,之后每次请求都带上,服务器解码这个token之后就知道是谁在访问了。优点是不占存储空间,后端解码即可。

70.浏览器实现本地存储的方式有哪几种?

  • cookie:存储大小4kb,会随请求发送到服务端,可设置过期时间。
  • localStorage:存储大小为5M,不参与请求,除非被清理,否则一直存在。
  • sessionStorage:存储大小为5M,不参与请求,页面关闭清除。
  • indexDB:存储大小没限制,不参与请求,除非被清理,否则一直存在,运行在浏览器上的非关系型数据库。

71.了解Service Worker嘛?

  • 是运行在浏览器背后的独立线程,可用于实现缓存功能,传输协议必须是HTTPS。使用Service-Worker实现缓存功能一般分为三个步骤:首先注册,然后监听install事件缓存需要的文件,最后拦截请求事件,如果缓存中已经有请求的数据就直接使用。

72.谈谈对浏览器缓存的理解?

  • 浏览器缓存是性能优化中最简单高效的一种,可以显著的减少网络传输所带来损耗,降低服务端压力。对于一个请求来说,可以分为发起网络请求、后端处理、浏览器响应三个步骤。浏览器缓存就可以做到直接使用缓存而不发起请求,或者发起了请求但后端存储的数据和缓存是一致的,就没必要传回来。

73.从哪些地方可以读取到浏览器缓存?

  • 浏览器缓存会从四个位置去读取,并且它们是有优先级的,会依次去查找,最后都没有找到才会去发起请求。
  1. Service Worker:和浏览器其他内建缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存且缓存是持续性的。
  2. Memory Cache:从内存中读取,速度快,不过缓存的持续性并不高,关闭页面后内存中的缓存会被释放,什么东西能进内存确定不了。
  3. Disk Cache:速度没有内存快,不过存储的容量和持续性会高很多,在浏览器缓存中硬盘的覆盖面是最大的。可以根据HTTP Header中的字段判断哪些资源需要缓存,哪些可以不请求直接使用,哪些已过期需要重新请求。并且即使在跨站点的情况下,相同地址的资源一旦被硬盘缓存下来,就不会再次去请求数据。
  4. Push Cache:是HTTP/2中的内容,存储时间很短暂,只在会话中,一旦会话结束就被释放。

74.什么是浏览器缓存策略?

  • 缓存策略分为两种:强缓存和协商缓存,都是通过设置HTTP-Header来实现的,在强缓存没有命中之后才会尝试协商缓存。
  • 强缓存:通过设置HTTP-Header的Expires和Cache-Control实现。强缓存表示在缓存期间不需要发起请求,状态码为200。
  • 协商缓存:通过设置HTTP-Header的Last-Modified和ETag实现。如果缓存过期了,就需要发起请求验证资源是否有更新。如果发起请求验证资源没有改变,返回状态304,并且更新浏览器缓存的有效期。

75.浏览器缓存适用的应用场景有哪些?

  • 频繁变动的资源:首先设置Cache-Control:no-cache,使浏览器每次都请求服务器,然后配合ETag或Last-Modified来验证资源是否有效,虽然请求数量没少,不过能显著减少响应的数据大小。
  • 不频繁变动的资源:一般使用打包工具会为生成的js、css等文件最后加上哈希值,只有当代码修改后才会生成新的文件名。所以我们可以给这些文件设置Cache-Control:max-age=31536000为一年的有效期,文件名变更了就下载新的文件,否则一直使用缓存。

76.什么是XSS?

  • 通过利用网页开发时留下的漏洞,恶意攻击者往Web页面里插入恶意 Script代码,当用户浏览时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。
  • 持久性:将攻击的代码通过服务端写入到数据库中,例如通过评论提交的评论里面是一段脚本,如果没做好防范就会提交到数据库里,可能导致其他用户会执行这段代码,会到攻击。
  • 非持久性:一般是通过修改URL参数的方式加入攻击代码,从而诱导用户访问链接从而进行攻击。

77.如何防范XSS?

  • 转义字符:将用户输入的内容,如引号、尖括号、斜杠进行转义;采用白名单过滤标签和标签属性,例如过滤script标签;使用CSP告诉浏览器限制外部资源可以加载和执行,开启CSP有两种方式:1. 设置HTTP-Header中的Content-sesurity-Policy。 2. 设置标签的方式。

78.什么是CSRF?

  • 中文的意思是跨站请求伪造。原理是攻击者构造出一个后端请求地址,诱导用户去点击发起请求。如果是登陆状态,服务端就会以为是用户在操作,从而进行相应的逻辑。
  • 简单的说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己以前认证过的站点并运行一些操作(如发邮件,发消息,甚至财产操作(如转账和购买商品))。因为浏览器之前认证过,所以被访问的站点会绝点是这是真正的用户操作而去运行。

79.如何防范CSRF?

  • 不让get请求对数据进行修改;不让第三方网站访问到用户的cookie,设置cookie的SameSite属性,不让cookie随跨域请求携带;组织第三方网站请求接口,验证Referer;Token验证,登陆后服务器下发一个随机token,之后的请求带上。

80.什么是点击劫持?

+是一种视觉欺骗的攻击手段,攻击者将需要攻击的网站通过iframe嵌入的方式嵌入到自己的网页里,将iframe设置为透明,在页面中透出一个按钮诱导用户点击。

81.如何防范点击劫持?

  • 现代浏览器设置HTTP响应头X-FRAME-OPTIONS,这个响应头就是为了防御点击劫持的;远古浏览器使用js防御,当通过iframe的方式加载页面时,让攻击者网站不显示内容。

82.什么是中间人攻击?

  • 就是在攻击者在服务端和客户端建立了连接,并让对方认为连接是安全的,攻击者不仅能获得双方的通信信息,还能修改通信信息。

83.如果防范中间人攻击?

  • 不要使用公共wifi;只使用https协议,并关闭http的访问。

84.你知道的性能优化方式有哪些?

  1. 文件压缩,减小资源大小
  2. 异步组件,按需加载
  3. 小图片转base64,减少请求
  4. 雪碧图,减少请求
  5. 选择合适的图片格式和尺寸
  6. 懒加载,按需加载
  7. css放最上面,js放在body最下面,渲染优化
  8. 事件节流,减少操作
  9. 减少Dom操作和避免回流,渲染优化
  10. 浏览器缓存,减少请求次数或响应数据
  11. 减少cookie的使用,减少请求携带大小

85.babel是如何将ES6代码转为ES5的?

  • 首先解析ES6的代码字符串,生成 ES6的AST语法树;
  • 然后将ES6的AST转为ES5的AST语法树;
  • 最后将ES5的AST转换成字符串代码。

86.有哪些方式可以提升webpack的打包速度?

  • loader:使用include和exclude指定搜索文件的范围。
  • babel-loader:配置后面加上loader:'babel-loader?cacheDirectory=true'将编译过的文件缓存起来,下次只需要编译更改过的代码文件即可。
  • HappyPack:使用这个插件开启loader多线程打包。
  • DllPlugin:将特定的类库提前打包然后引入,减少打包类库的次数,只有当类库更新版本才进行重新打包。
  • resolve.alias:配置别名,更快找到路径。
  • module.noParse:确定这个文件没有其他依赖时,让webpack打包时不扫描该文件。

87.有哪些方式可以减小webpack的打包后的体积?

  • Scope Hoisting:分析模块之间的依赖关系,尽可能的把打包出来的模块合并到一个函数里。
  • Tree Shaking:删除项目中未被引用的代码。

88.对HTTP协议的理解?

  • 是TCP/IP协议家族的子集,属于通信传输流中链路层、网络层、传输层、应用层中的应用层,主要职责是生成针对目标web服务器的HTTP请求报文和请求内容的处理。
  • HTTP是无状态的协议,不对请求和响应之间的通信状态进行保存,响应完成后就会断开连接。
  • 参考链接: blog.csdn.net/zjgyjd/arti…

89.什么是持久连接以及管线化?

+持久连接:在HTTP/1.1之前的时代,每一次HTTP请求就需要先TCP建立三次握手,传输完毕后就断开连接,会增加很多的通信开销。HTTP/1.1增加了持久连接,也就是说在一次TCP连接里面可以发送多次HTTP请求,只要任意一端没有明确提出断开连接,则保持TCP的连接状态,也就是响应头里面的Connection:keep-alive。

  • 管线化:在持久连接里处理HTTP的方式是,发送响应完成后才能发起下一个请求,而管线化解决的问题是可以一次发起多个HTTP请求,且可以同时返回多次响应结果。

90.为什么发起HTTP请求前需要TCP三次握手?

  • 为了让客户端和服务端都能确定彼此发起和响应的能力是否靠谱。
  • 客户端首先发送证明客户端有发送的能力,服务端接受后返回证明服务端有响应和发送的能力,但这个时候还不能知道客户端的响应能力,所以客户端再响应之后,表明彼此连接的通道是顺畅的,然后HTTP请求就可以传输数据了。

91.为什么关闭HTTP请求前需要TCP四次挥手?

  • 关闭连接是双向的,客户端和服务器都可以提出,四次挥手是为了不让关闭太仓促,保证可靠性。
  • 如客户端首先会告知服务器申请关闭连接,服务器收到后告诉客户端收到了,不过我还没有准备好,让客户端等等。服务端数据发送数据完毕后,再次告诉客户端,我准备关闭连接了。客户端收到后怕网络不好,服务器不知道要关闭,所以第四次发送信息确认,服务器收到后断开连接,客户端也断开连接。

92.HTTP请求报文和响应报文里分别有什么?

  • 报文的结构大致是两部分,报文首部,一个空行,和报文主体,报文主体不一定非要有。
  • 请求报文:包括了请求行,里面包括请求的方法,协议版本;各种首部的字段,例如服务器域名、客户端信息、缓存信息、压缩传输的方式等。
  • 响应报文:包括了状态行,协议版本,响应的状态码;各种首部的字段,如ETag、日期、内容类型等,以及响应的报文主体。

93.http1.0和http1.1的区别?

  • http1.1加如了持久连接。
  • 加入了更多的请求头、响应头(缓存方面的)。
  • 新增了一些错误状态码、如409(请求的资源和资源当前状态发生冲突)、410(服务器上的某个资源被永久性的删除)。

94. http和https的区别?

  • url开头不一致是最明显的区分;其次http没有https安全,http没有经过SSL/TLS加密、身份验证;还有默认的端口不一样,http是80、https是443,https需要证书,https是防止中间人攻击方式的一种。

95.为什么https更安全?

  • 一般使用公钥加密或私钥加密:
  1. 公钥加密双方都需要事先知道一个都知道加密方式的密钥,优点是加密速度快,缺点是过程中可能会被窃取,安全性没有非对称加密高。
  2. 非对称加密会加入公钥和私钥,客户端使用第三方证书去服务端获取公钥,然后用获取到的公钥把共享密钥进行加密发送给服务端,服务端使用私钥解密出共享密钥,服务端用获取到的共享密钥进行消息的加密,客户端用用共享密钥进行解密。

96.常见的响应状态码有哪些?

  • 大致可以分为2开头表示成功、3开头表示重定向、4开头客户端错误、5开头服务器错误。
  1. 204:如当浏览器发出请求处理后,返回204响应,表示客户端显示的页面不发生更新。
  2. 206:客户端只要想要响应内容中的一部分,服务端成功执行了这次响应。响应报文中的Content-Range则指定了哪部分的内容。
  3. 301:永久重定向,表示请求的资源已被分配到了新的URI,以后使用新的吧
  4. 302:临时重定向,表示请求的资源已被分配到了新的URI,现在使用新的吧
  5. 303:临时重定向,表示请求的资源已被分配到了新的URI,请使用get方法获取资源。
  6. 304:服务端找不到根据客户端发送附带条件的请求。(附带条件指get请求报文中包含If-Match、If-Modified-Since、If-None-Match、If-Range、If-Unmodified-Since中的一个)
  7. 400:请求报文存在语法错误。
  8. 403:请求资源被服务器拒绝。
  9. 503:表明服务器暂时处于超负载或正在停机维护,无法处理请求。

97.get和post的区别?

  • get回退不会重新发起请求,post会;
  • get默认会被浏览器主动缓存,post不会;
  • get只能进行url编码,post支持多种编码方式;
  • get的请求参数会被拼接到url后面,post放在request-body中;
  • get产生一个tcp数据包,post会产生两个tcp包;
  • get主要是应用为获取资源,post主要是应用于传输或修改资源

98.什么是UDP协议?

  • 属于通信传输流中的传输层,UDP是面向无连接的,传输双方没有确认机制,也就是说你要传就传吧,没有HTTP那样需要事先三从握手。
  • 缺点是不能保证数据传输的可靠性;
  • 优点是报文头信息少开销小,支持一对多、多对多、多对一的传输方式,传输实时性强。常用于直播以及游戏。