我总结的前端面试题

前言:写这篇文章的主要目的是为了方便自己,也想给大家提供一个资料,有不对的地方,大家多多指出。

自我介绍(仅供参考)

注意要点(听清楚面试官的问题,想一想再作答,语速放慢,遇到不会的问题不要慌,要主动问面试官问题的答案,目光直视面试官)

面试官上午好,我叫**,今天来应聘贵公司的XXX岗位。我从事前端开发三年多,有X年多的Vue开发经验,在上家公司主要从事后台管理系统,混合App等项目开发。平常喜欢逛一些技术社区丰富自己的技术,像思否,CSDN之类,并且自己也独立开发了个人博客网站,记录自己的工作总结和学习心得。 我的性格比较外向,跟同事朋友相处时比较融洽,在工作中代码开发时我喜欢全心全意的投入,对于工作我总抱着认真负责的态度。面试官,以上是我的介绍,谢谢。(根据自己的具体情况来介绍,介绍一下自己的优势)

项目具体介绍

这一块一般都会问的,特别是你的年限特别长的时候,你最好把你最近的一个项目好好准备一下,讲一下你在这个项目中当任的角色,这个项目具体内容,亮点和难点。

HTML、CSS相关

一、介绍一下css盒模型

CSS盒模型本质上是一个盒子,封装周围的HTML元素,它包括:边距margin,边框border,填充padding,和实际内容content。

有两种盒模型,标准 盒模型和 怪异 盒模型,区别在于,元素的宽高大小表现为content的大小,而怪异盒模型则表现为content + padding + border的大小

二、样式框架的原理,布局方法有哪些,flex,grid,还有吗?

正常流布局 浏览器默认的HTML布局方式,就是按照文档的顺序一个一个显示出来,块元素独占一行,行内元素共享一行

浮动布局 使用 float 属性,使元素脱离文档流,浮动起来。

定位布局 通过 position 属性来进行定位。(这里常考绝对定位于相对定位基准问题,下面再讲)

使用display布局 在css中实现页面布局的主要方法是设定display属性的值。此属性允许我们更改默认的显示方式。 通过将一些内容从block转换为inline(反之亦然)来更改默认表示形式,更重要的是可以通过设置flex 或 grid 进行布局:

flex布局 通过容器和轴进行布局设置。其中,容器分为父容器和子容器。轴分为主轴和交叉轴。

grid 网格布局:可以实现二维布局方式,和 table 布局差不多,基本概念-网格线(Grid Lines) 网格的水平和垂直的分界线 、网格轨道(Grid Track) 好比表格中行或列,分为grid column和grid row 、网格单元格(Grid Cell) 好比表格中的单元格 、网格区域(Grid Area) 好比表格合并单元格后的区域(用的不是很多)

多列布局 : 一种把内容按列排序的方式,就像文本在报纸上排列那样。使用 column-count属性设置需要多少列,也可以使用column-width以至少某个宽度的尽可能多的列来填充容器。

三、css绝对定位和相对定位都是以谁为基准?

定位position的几个属性要清楚:

  • 静态定位(Static positioning)是每个元素默认的属性——它表示“将元素放在文档布局流的默认位置。
  • 相对定位(Relative positioning)允许我们相对于元素在正常的文档流中的位置移动它——包括将两个元素叠放在页面上。这对于微调和精准设计(design pinpointing)非常有用。
  • 绝对定位(Absolute positioning)脱离文档流。我们可以将元素相对于页面的 元素边缘固定,或者相对于该元素的最近被定位祖先元素(nearest positioned ancestor element)。绝对定位在创建复杂布局效果时非常有用,例如通过标签显示和隐藏的内容面板或者通过按钮控制滑动到屏幕中的信息面板。
  • 固定定位(Fixed positioning)与绝对定位非常类似,但是它是将一个元素相对浏览器视口固定。 这在创建类似在整个页面滚动过程中总是处于屏幕的某个位置的导航菜单时非常有用。
  • 粘性定位(Sticky positioning)它会让元素先保持和position: static一样的定位,当它的相对视口位置(offset from the viewport)达到某一个预设值时,他就会再像position: fixed一样定位(某些网站头导航栏滚动到一定位置固定到屏幕)。

再回答这个问题:

  • absolute 绝对定位 相对于最近的已定位的祖先元素, (有已定位指position不是static的元素祖先元素)如果无已定位祖先元素, 以body元素为偏移参照基准, 完全脱离了标准文档流。

  • relative:相对定位元素的定位是相对其正常位置。设置了relative的元素仍然处在文档流中,元素的宽高不变,设置偏移量也不会影响其他元素的位置。

共同点:改变行内元素的呈现方式,都脱离了文档流; 不同点:absolute的”根元素“是可以设置的,fixed的“根元素”固定为浏览器窗口

四、浮动布局和flex布局相比有什么优缺点?

flex:优点在于其容易上手,根据flex规则很容易达到某个布局效果。缺点是:浏览器兼容性比较差,只能兼容到ie9及以上。

浮动:浮动元素是脱离文档流,要做清除浮动,这个处理不好的话,会带来很多问题,比如高度塌陷等。浮动布局的优点就是比较简单,兼容性也比较好。只要清除浮动做的好,是没有什么问题的。

五、清除浮动方法

  • 给父级定义height 缺点:扩展性不好

  • 父级 overflow:hidden 定位 浮动 开启BFC布局 浮动/定位盒子的特点高宽都由内容撑开 缺点:IE6会失效,添加样式zoom:1;触发拥有布局(haslayout)


  • 标签清除浮动 
    缺点:IE6不支持

  • 空标签清除浮动 

     缺点:违反了结构行为样式相分离的原则.

  • 伪元素清除浮动 clearfix

clearfix:{
    *zoom:1; /*IE6 IE7不支持伪类元素  开启haslayout*/
}
clearfix::after{
    content:'';
    display:block;
    clear:both;
}

六、bfc是什么,怎么样形成bfc,bfc有哪些用?

BFC Block Formatting Context(BFC | 块级格式化上下文),是Web页面中盒模型布局的CSS渲染模式,是一个隔离的独立容器。 

怎样形成一个BFC? 块级格式化上下文由以下之一创建:

  • 根元素或其它包含它的元素
  • 浮动 (元素的 float 不是 none)
  • 绝对定位的元素 (元素具有 position 为 absolute 或 fixed)
  • 非块级元素具有 display: inline-block,table-cell, table-caption, flex, inline-flex
  • 块级元素具有overflow ,且值不是 visible

BFC用处

  1. 清除浮动(规则6)
  2. 布局:自适应两栏布局(规则4、5)
  3. 防止垂直margin合并(规则5)

BFC规则:

  1. 内部的Box会在垂直方向,一个接一个地放置。
  2. Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠
  3. 每个元素的左外边缘(margin-left), 与包含块的左边(contain box left)相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。除非这个元素自己形成了一个新的BFC。
  4. BFC的区域不会与float box重叠。
  5. BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
  6. 计算BFC的高度时,浮动元素也参与计算

七、div水平垂直居中的方法

实现水平垂直布局基本就是将上面几种方式结合使用,这里总结了7种常用的布局方法,其公共的 CSS 代码如下所示:

body {
  margin: 0;
}
.parent {
  height: 500px;
  width: 500px;
  background-color: #eebefa;
  margin: 0 auto;
}
.child {
  height: 300px;
  width: 300px;
  background-color: #f783ac;
}

其 HTML 结构也是固定的,就是一个父级包裹一个子级,这里的子级是固定的300px*300px,代码如下:


  

最终的实现效果如下:

1. 行内块级水平垂直居中方案

步骤如下:

  • 容器元素行高等于容器高度

  • 通过 text-align: center; 实现水平居中

  • 将子级元素设置为水平块级元素

  • 通过 vertical-align: middle; 实现垂直居中

实现CSS代码如下:

.parent {
  /* 1. 设置行高等于容器高度 */
  line-height: 500px;
  /* 通过 text-align: center; 实现水平居中 */
  text-align: center;
}
.child {
  /* 将子级元素设置为水平块级元素 */
  display: inline-block;
  /* 通过 vertical-align: middle; 实现垂直居中 */
  vertical-align: middle;
}

2. 定位实现水平垂直居中方案(一)

步骤如下:

  • 使子元素相对于容器元素定位

  • 子元素开启绝对定位

  • 设置该元素的偏移量,值为50% 减去宽度/高度的一半

实现CSS代码如下:

.parent {
  /* 1. 使子元素相对于本元素定位 */
  position: relative;
}
.child {
  /* 2. 开启绝对定位 */
  position: absolute;
  /* 3. 设置该元素的偏移量,值为 50%减去宽度/高度的一半 */
  left: calc(50% - 150px);
  top: calc(50% - 150px);
}

3. 定位实现水平垂直居中方案(二)

步骤如下:

  • 使子元素相对于容器元素定位

  • 子元素开启绝对定位

  • 设置该元素的偏移量,值为50%

  • 通过外边距-值的方式将元素移动回去

实现CSS代码如下:

.parent {
  /* 1. 使子元素相对于本元素定位 */
  position: relative;
}
.child {
  /* 2. 开启绝对定位 */
  position: absolute;
  /* 3. 设置该元素的偏移量,值为 50% */
  left: 50%;
  top: 50%;
  margin-left: -150px;
  margin-top: -150px;
}

4. 定位实现水平垂直居中方案(三)

步骤如下:

  • 使子元素相对于容器元素定位

  • 子元素开启绝对定位

  • 将子元素拉满整个容器

  • 通过margin:auto实现水平垂直居中

实现CSS代码如下:

.parent {
  /* 1. 使子元素相对于本元素定位 */
  position: relative;
}
.child {
  /* 2. 开启绝对定位 */
  position: absolute;
  /* 3. 将子元素拉满整个容器 */
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  /* 4. 通过 margin:auto 实现水平垂直居中 */
  margin: auto;
}

5. 定位实现水平垂直居中方案(四)

步骤如下:

  • 使子元素相对于容器元素定位

  • 子元素开启绝对定位

  • 设置该元素的偏移量,值为50%

  • 通过 translate 反向偏移的方式,实现居中

实现 CSS 代码如下:

.parent {
  /* 1. 使子元素相对于本元素定位 */
  position: relative;
}
.child {
  /* 2. 开启绝对定位 */
  position: absolute;
  /* 3. 设置该元素的偏移量,值为 50%*/
  left: 50%;
  top: 50%;
  /* 通过translate反向偏移的方式,实现居中 */
  transform: translate(-50%, -50%);
}

6. Flex方案

步骤如下:

  • 将元素设置为 Flex 布局

  • 通过 justify-content: center 以及 align-items: center 实现或者 margin: auto; 实现。

实现CSS代码如下:

.parent {
  /* 1. 将元素设置为 Flex 布局 */
  display: flex;
  /* 2. 通过 justify-content 以及 align-items: center 实现 */
  /* justify-content: center;
  align-items: center; */
}
.child {
  /* 或者通过 margin auto 实现 */
  margin: auto;
}

7. Grid方案

Grid 方案的实现方式相对来说比较简单,方式也较多。

实现CSS代码如下:

.parent {
  /* 1. 元素设置为Grid 元素 */
  display: grid;
  /* 通过 items 属性实现*/
  /* align-items: center; */
  /* justify-items: center; */
  /* items 的缩写 */
  /* place-items: center; */

  /* 或者通过 content 属性 */
  /* align-content: center; */
  /* justify-content: center; */
  /* content 的缩写 */
  /* place-content: center; */
}
.child {
  /* 或者通过 margin auto 实现 */
  /* margin: auto; */
  /* 或者通过 self 属性 */
  /* align-self: center;
  justify-self: center; */
  /* self 的缩写 */
  place-self: center;
}

实现水平垂直居中布局的方式大多是通过上面两种布局的方式相结合。

八、浏览器渲染机制、重绘、重排

网页生成过程:

  • HTML被HTML解析器解析成DOM
  • css则被css解析器解析成CSSOM
  • 结合DOM树和CSSOM树,生成一棵渲染树(Render Tree)
  • 生成布局(flow),即将所有渲染树的所有节点进行平面合成
  • 将布局绘制(paint)在屏幕上

重排(也称回流):DOM的变化影响了元素的几何信息(DOM对象的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排。 触发:

  1. 添加或者删除可见的DOM元素
  2. 元素尺寸改变——边距、填充、边框、宽度和高度

重绘: 当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘。 触发:

  • 改变元素的color、background、box-shadow等属性

重排优化建议:

  1. 分离读写操作
  2. 样式集中修改
  3. 缓存需要修改的DOM元素
  4. 尽量只修改position:absolutefixed元素,对其他元素影响不大
  5. 动画开始GPU加速,translate使用3D变化

transform 不重绘,不回流 是因为transform属于合成属性,对合成属性进行transition/animate动画时,将会创建一个合成层。这使得动画元素在一个独立的层中进行渲染。当元素的内容没有发生改变,就没有必要进行重绘。浏览器会通过重新复合来创建动画帧。

JS相关

一、display、visibility、opacity的区别

display: none;

1. DOM 结构:浏览器不会渲染 display 属性为 none 的元素,不占据空间;
2. 事件监听:无法进行 DOM 事件监听;
3. 性能:动态改变此属性时会引起重排,性能较差;
4. 继承:不会被子元素继承,毕竟子类也不会被渲染;
5. transition:transition 不支持 display。

visibility: hidden;

1. DOM 结构:元素被隐藏,但是会被渲染不会消失,占据空间;
2. 事件监听:无法进行 DOM 事件监听;
3. 性 能:动态改变此属性时会引起重绘,性能较高;
4. 继 承:会被子元素继承,子元素可以通过设置 visibility: visible; 来取消隐藏;
5. transition:visibility 会立即显示,隐藏时会延时

opacity: 0;

1. DOM 结构:透明度为 100%,元素隐藏,占据空间;
2. 事件监听:可以进行 DOM 事件监听;
3. 性 能:提升为合成层,不会触发重绘,性能较高;
4. 继 承:会被子元素继承,且,子元素并不能通过 opacity: 1 来取消隐藏;
5. transition:opacity 可以延时显示和隐藏

二、js数据类型、typeof、instanceof、类型转换

  1. string、number、boolean、null、undefined、object(function、array)、symbol(ES10 BigInt)
  2. typeof 主要用来判断数据类型 返回值有string、boolean、number、function、object、undefined。
  3. instanceof 判断该对象是谁的实例。
  4. null表示空对象 undefined表示已在作用域中声明但未赋值的变量

三、闭包(高频)

闭包是指有权访问另一个函数作用域中的变量的函数 ——《JavaScript高级程序设计》

当函数可以记住并访问所在的词法作用域时,就产生了闭包,

即使函数是在当前词法作用域之外执行 ——《你不知道的JavaScript》

  • 闭包用途:
    1. 能够访问函数定义时所在的词法作用域(阻止其被回收)
    2. 私有化变量
    3. 模拟块级作用域
    4. 创建模块
  • 闭包缺点:会导致函数的变量一直保存在内存中,过多的闭包可能会导致内存泄漏

四、原型、原型链(高频)

原型: 对象中固有的__proto__属性,该属性指向对象的prototype原型属性。

原型链: 当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型对象里找这个属性,这个原型对象又会有自己的原型,于是就这样一直找下去,也就是原型链的概念。原型链的尽头一般来说都是Object.prototype所以这就是我们新建的对象为什么能够使用toString()等方法的原因。

特点: JavaScript对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变。

五、this指向、new关键字

this对象是是执行上下文中的一个属性,它指向最后一次调用这个方法的对象,在全局函数中,this等于window,而当函数被作为某个对象调用时,this等于那个对象。 在实际开发中,this 的指向可以通过四种调用模式来判断。

  1. 函数调用,当一个函数不是一个对象的属性时,直接作为函数来调用时,this指向全局对象。
  2. 方法调用,如果一个函数作为一个对象的方法来调用时,this指向这个对象。
  3. 构造函数调用,this指向这个用new新创建的对象。
  4. 第四种是 apply 、 call 和 bind 调用模式,这三个方法都可以显示的指定调用函数的 this 指向。apply接收参数的是数组,call接受参数列表,`` bind方法通过传入一个对象,返回一个 this 绑定了传入对象的新函数。这个函数的 this指向除了使用new `时会被改变,其他情况下都不会改变。

六、new

  1. 首先创建了一个新的空对象
  2. 设置原型,将对象的原型设置为函数的prototype对象。
  3. 让函数的this指向这个对象,执行构造函数的代码(为这个新对象添加属性)
  4. 判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。

七、作用域、作用域链、变量提升

作用域负责收集和维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。(全局作用域、函数作用域、块级作用域)。 作用域链就是从当前作用域开始一层一层向上寻找某个变量,直到找到全局作用域还是没找到,就宣布放弃。这种一层一层的关系,就是作用域链

八、EventLoop

JS是单线程的,为了防止一个函数执行时间过长阻塞后面的代码,所以会先将同步代码压入执行栈中,依次执行,将异步代码推入异步队列,异步队列又分为宏任务队列和微任务队列,因为宏任务队列的执行时间较长,所以微任务队列要优先于宏任务队列。微任务队列的代表就是,Promise.thenMutationObserver,宏任务的话就是setImmediate setTimeout setInterval

九、原生ajax

ajax是一种异步通信的方法,从服务端获取数据,达到局部刷新页面的效果。 过程:

  1. 创建XMLHttpRequest对象;
  2. 调用open方法传入三个参数 请求方式(GET/POST)、url、同步异步(true/false);
  3. 监听onreadystatechange事件,当readystate等于4时返回responseText;
  4. 调用send方法传递参数。

十、事件冒泡、捕获(委托)

  • 事件冒泡指在在一个对象上触发某类事件,如果此对象绑定了事件,就会触发事件,如果没有,就会向这个对象的父级对象传播,最终父级对象触发了事件。
  • 事件委托本质上是利用了浏览器事件冒泡的机制。因为事件在冒泡过程中会上传到父节点,并且父节点可以通过事件对象获取到目标节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件,这种方式称为事件代理

event.stopPropagation() 或者 ie下的方法 event.cancelBubble = true; //阻止事件冒泡

十一、浅拷贝和深拷贝

  • 浅拷贝
// 第一层为深拷贝
Object.assign()
Array.prototype.slice()
扩展运算符 ...
  • 深拷贝
JSON.parse(JSON.stringify())

递归函数
function cloneObject(obj) { var newObj = {} //如果不是引用类型,直接返回 if (typeof obj !== 'object') { return obj } //如果是引用类型,遍历属性 else { for (var attr in obj) { //如果某个属性还是引用类型,递归调用 newObj[attr] = cloneObject(obj[attr]) } } return newObj }

如何实现一个深拷贝
详细解析赋值、浅拷贝和深拷贝的区别

十二、数组去重的方法

1.ES6 的 Set

let arr = [1,1,2,3,4,5,5,6]
let arr2 = [...new Set(arr)]

2.reduce()

let arr = [1,1,2,3,4,5,5,6]
let arr2 = arr.reduce(function(ar,cur) {
  if(!ar.includes(cur)) {
    ar.push(cur)
  }

  return ar
},[])

3.filter()

// 这种方法会有一个问题:[1,'1']会被当做相同元素,最终输入[1]
let arr = [1,1,2,3,4,5,5,6]
let arr2 = arr.filter(function(item,index) {
  // indexOf() 方法可返回某个指定的 字符串值 在字符串中首次出现的位置
  return arr.indexOf(item) === index
})

十三、介绍下 promise.all

      Promise.all()方法将多个Promise实例包装成一个Promise对象(p),接受一个数组(p1,p2,p3)作为参数,数组中不一定需要都是Promise对象,但是一定具有Iterator接口,如果不是的话,就会调用Promise.resolve将其转化为Promise对象之后再进行处理。
      使用Promise.all()生成的Promise对象(p)的状态是由数组中的Promise对象(p1,p2,p3)决定的。

  1. 如果所有的Promise对象(p1,p2,p3)都变成fullfilled状态的话,生成的Promise对象(p)也会变成fullfilled状态,
    p1,p2,p3三个Promise对象产生的结果会组成一个数组返回给传递给p的回调函数。
  2. 如果p1,p2,p3中有一个Promise对象变为rejected状态的话,p也会变成rejected状态,第一个被rejected的对象的返回值会传递给p的回调函数。
    Promise.all()方法生成的Promise对象也会有一个catch方法来捕获错误处理,但是如果数组中的Promise对象变成rejected状态时,
    并且这个对象还定义了catch的方法,那么rejected的对象会执行自己的catch方法。
    并且返回一个状态为fullfilled的Promise对象,Promise.all()生成的对象会接受这个Promise对象,不会返回rejected状态。

十四、async 和 await

主要考察宏任务和微任务,搭配promise,询问一些输出的顺序

原理:async 和 await 用了同步的方式去做异步,async 定义的函数的返回值都是 promise,await 后面的函数会先执行一遍,然后就会跳出整个 async 函数来执行后面js栈的代码

对前端性能优化有什么了解?一般都通过那几个方面去优化的?

前端性能优化的七大手段

  1. 减少请求数量
  2. 减小资源大小
  3. 优化网络连接
  4. 优化资源加载
  5. 减少重绘回流
  6. 性能更好的API
  7. webpack优化

十五、实现 new 方法

/*
* 1.创建一个空对象
* 2.链接到原型
* 3.绑定this值
* 4.返回新对象
*/
// 第一种实现
function createNew() {
    let obj = {}  // 1.创建一个空对象

    let constructor = [].shift.call(arguments)  
    // let [constructor,...args] = [...arguments]  

    obj.__proto__ = constructor.prototype  // 2.链接到原型

    let result = constructor.apply(obj, arguments)  // 3.绑定this值
    // let result = constructor.apply(obj, args)   

    return typeof result === 'object' ? result : obj  // 4.返回新对象
}

function People(name,age) {
    this.name = name
    this.age = age
}

let peo = createNew(People,'Bob',22)
console.log(peo.name)
console.log(peo.age)

十六、实现 Promise

// 未添加异步处理等其他边界情况
// ①自动执行函数,②三个状态,③then
class Promise {
  constructor (fn) {
    // 三个状态
    this.state = 'pending'
    this.value = undefined
    this.reason = undefined
    let resolve = value => {
      if (this.state === 'pending') {
        this.state = 'fulfilled'
        this.value = value
      }
    }
    let reject = value => {
      if (this.state === 'pending') {
        this.state = 'rejected'
        this.reason = value
      }
    }
    // 自动执行函数
    try {
      fn(resolve, reject)
    } catch (e) {
      reject(e)
    }
  }
  // then
  then(onFulfilled, onRejected) {
    switch (this.state) {
      case 'fulfilled':
        onFulfilled(this.value)
        break
      case 'rejected':
        onRejected(this.reason)
        break
      default:
    }
  }
}

十七、实现一个 call 函数

// 思路:将要改变this指向的方法挂到目标this上执行并返回
Function.prototype.mycall = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('not funciton')
  }
  context = context || window
  context.fn = this
  let arg = [...arguments].slice(1)
  let result = context.fn(...arg)
  delete context.fn
  return result
}

十八、实现一个 apply 函数

// 思路:将要改变this指向的方法挂到目标this上执行并返回
Function.prototype.myapply = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('not funciton')
  }
  context = context || window
  context.fn = this
  let result
  if (arguments[1]) {
    result = context.fn(...arguments[1])
  } else {
    result = context.fn()
  }
  delete context.fn
  return result
}

十九、实现一个 bind 函数

// 思路:类似call,但返回的是函数
Function.prototype.mybind = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  let _this = this
  let arg = [...arguments].slice(1)
  return function F() {
    // 处理函数使用new的情况
    if (this instanceof F) {
      return new _this(...arg, ...arguments)
    } else {
      return _this.apply(context, arg.concat(...arguments))
    }
  }
}

更多介绍:bind方法的实现

回到顶部

二十、浅拷贝、深拷贝的实现

浅拷贝:// 1. ...实现
let copy1 = {...{x:1}}

// 2. Object.assign实现
let copy2 = Object.assign({}, {x:1})
深拷贝:
// 1. JOSN.stringify()/JSON.parse()  
// 缺点:拷贝对象包含 正则表达式,函数,或者undefined等值会失败
let obj = {a: 1, b: {x: 3}}
JSON.parse(JSON.stringify(obj))

// 2. 递归拷贝
function deepClone(obj) {
  let copy = obj instanceof Array ? [] : {}
  for (let i in obj) {
    if (obj.hasOwnProperty(i)) {
      copy[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i]
    }
  }
  return copy
}
// 基础版
function deepClone(origin) {
  if (origin == undefined || typeof origin != 'object') {
    return origin
  }
  var result = new origin.constructor() 
  for (var k in origin) {
    result[k] = typeof origin[k] === 'object' ? deepClone(origin[k]) : origin[k]
  }
  return result
}

// 解决循环引用版
function deepClone(origin, wm = new WeakMap()) {
  if (origin == undefined || typeof origin != 'object') {
    return origin
  }
  var val = wm.get(origin)
  // 如果 wm 存在,则直接返回
  if (val) {
    return wm.get(origin)
  }
  var target = new origin.constructor()
  wm.set(origin, target)
  for (var k in origin) {
    target[k] = typeof origin[k] === 'object' ? deepClone(origin[k], wm) : origin[k]
  }
  return target
}

二十一、实现一个防抖函数

// 思路:在规定时间内未触发第二次,则执行
function debounce (fn, delay) {
  // 利用闭包保存定时器
  let timer = null
  return function () {
    let context = this
    let arg = arguments
    // 在规定时间内再次触发会先清除定时器后再重设定时器
    clearTimeout(timer)
    timer = setTimeout(function () {
      fn.apply(context, arg)
    }, delay)
  }
}

function fn () {
  console.log('防抖')
}
addEventListener('scroll', debounce(fn, 1000))

二十二、实现一个节流函数

// 基础版1:时间戳(第一次触发会执行,但不排除不执行的可能,请思考一下哦)
function throttle(fn, delay) {
  var prev = Date.now()
  return function(...args) {
    var dist = Date.now() - prev
    if (dist >= delay) {
      fn.apply(this, args)
      prev = Date.now()
    }
  }
}

// 基础版2:定时器(最后一次也会执行)
function throttle(fn, delay) {
  var timer = null
  return function(...args) {
    var that = this
    if(!timer) {
      timer = setTimeout(function() {
        fn.apply(this, args)
        timer = null
      }, delay)
    }
  }
}

// 进阶版:开始执行、结束执行
function throttle(fn, delay) {
  var timer = null
  var prev = Date.now()
  return function(...args) {
    var that = this
    var remaining = delay - (Date.now() - prev)  // 剩余时间
    if (remaining <= 0) {  // 第 1 次触发
      fn.apply(that, args)
      prev = Date.now()
    } else { // 第 1 次之后触发
      timer && clearTimeout(timer)
      timer = setTimeout(function() {
        fn.apply(that, args)
      }, remaining)
    }
  }
}

function fn () {
  console.log('节流')
}
addEventListener('scroll', throttle(fn, 1000))

二十三、instanceof 的原理

// 思路:右边变量的原型存在于左边变量的原型链上
function instanceOf(left, right) {
  let leftValue = left.__proto__
  let rightValue = right.prototype
  while (true) {
    if (leftValue === null) {
      return false
    }
    if (leftValue === rightValue) {
      return true
    }
    leftValue = leftValue.__proto__
  }
}

二十四、柯里化函数的实现

柯里化函数的定义:将多参数的函数转换成单参数的形式
柯里化函数实现的原理:利用闭包原理在执行可以形成一个不销毁的作用域,然后把需要预先处理的内容都储存在这个不销毁的作用域中,并且返回一个最少参数函数。

第一种:固定传入参数,参数够了才执行

/**
 * 实现要点:柯里化函数接收到足够参数后,就会执行原函数,那么我们如何去确定何时达到足够的参数呢?
 * 柯里化函数需要记住你已经给过他的参数,如果没给的话,则默认为一个空数组。
 * 接下来每次调用的时候,需要检查参数是否给够,如果够了,则执行fn,没有的话则返回一个新的 curry 函数,将现有的参数塞给他。
 * 
 */
// 待柯里化处理的函数
let sum = (a, b, c, d) => {
  return a + b + c + d
}

// 柯里化函数,返回一个被处理过的函数 
let curry = (fn, ...arr) => {  // arr 记录已有参数
  return (...args) => {   // args 接收新参数
    if (fn.length <= (...arr,...args)) {  // 参数够时,触发执行
      return fn(...arr, ...args)
    } else {  // 继续添加参数
      return curry(fn, [...arr, ...args])
    }
  }
}

var sumPlus = curry(sum)
sumPlus(1)(2)(3)(4)
sumPlus(1, 2)(3)(4)
sumPlus(1, 2, 3)(4)

第二种:不固定传入参数,随时执行

/**
 * 当然了,柯里化函数的主要作用还是延迟执行,执行的触发条件不一定是参数个数相等,也可以是其他的条件。
 * 例如参数个为0的情况,那么我们需要对上面curry函数稍微做修改
 */
// 待柯里化处理的函数
let sum = arr => {
  return arr.reduce((a, b) => {
    return a + b
  })
}

let curry = (fn, ...arr) => {  // arr 记录已有参数
  return (...args) => {  // args 接收新参数
    if (args.length === 0) {  // 参数为空时,触发执行
      return fn(...arr, ...args)
    } else {  // 继续添加参数
      return curry(fn, ...arr, ...args)
    }
  }
}

var sumPlus = curry(sum)
sumPlus(1)(2)(3)(4)()
sumPlus(1, 2)(3)(4)()
sumPlus(1, 2, 3)(4)()

参考链接:js如何用一句代码实现函数的柯里化(ES6)

二十五、Object.create 的基本实现原理

// 思路:将传入的对象作为原型
function create(obj) {
  function F() {}
  F.prototype = obj
  return new F()
}

二十六、实现一个基本的 Event Bus

// 组件通信,一个触发与监听的过程
class EventEmitter {
  constructor () {
    // 存储事件
    this.events = this.events || new Map()
  }
  // 监听事件
  addListener (type, fn) {
    if (!this.events.get(type)) {
      this.events.set(type, fn)
    }
  }
  // 触发事件
  emit (type) {
    let handle = this.events.get(type)
    handle.apply(this, [...arguments].slice(1))
  }
}

// 测试
let emitter = new EventEmitter()
// 监听事件
emitter.addListener('ages', age => {
  console.log(age)
})
// 触发事件
emitter.emit('ages', 18)  // 18

二十七、实现一个双向数据绑定

let obj = {}
let input = document.getElementById('input')
let span = document.getElementById('span')
// 数据劫持
Object.defineProperty(obj, 'text', {
  configurable: true,
  enumerable: true,
  get() {
    console.log('获取数据了')
  },
  set(newVal) {
    console.log('数据更新了')
    input.value = newVal
    span.innerHTML = newVal
  }
})
// 输入监听
input.addEventListener('keyup', function(e) {
  obj.text = e.target.value
})

详细的实现见:这应该是最详细的响应式系统讲解了

二十八、实现一个简单路由

// hash路由
class Route{
  constructor(){
    // 路由存储对象
    this.routes = {}
    // 当前hash
    this.currentHash = ''
    // 绑定this,避免监听时this指向改变
    this.freshRoute = this.freshRoute.bind(this)
    // 监听
    window.addEventListener('load', this.freshRoute, false)
    window.addEventListener('hashchange', this.freshRoute, false)
  }
  // 存储
  storeRoute (path, cb) {
    this.routes[path] = cb || function () {}
  }
  // 更新
  freshRoute () {
    this.currentHash = location.hash.slice(1) || '/'
    this.routes[this.currentHash]()
  }
}

二十九、实现懒加载

let imgs =  document.querySelectorAll('img')
// 可视区高度
let clientHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
function lazyLoad () {
  // 滚动卷去的高度
  let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
  for (let i = 0; i < imgs.length; i ++) {
    // 图片在可视区冒出的高度
    let x = clientHeight + scrollTop - imgs[i].offsetTop
    // 图片在可视区内
    if (x > 0 && x < clientHeight+imgs[i].height) {
      imgs[i].src = imgs[i].getAttribute('data')
    }
  }
}
// addEventListener('scroll', lazyLoad) or setInterval(lazyLoad, 1000)

三十、手写实现 AJAX

// 1. 简单流程

// 实例化
let xhr = new XMLHttpRequest()
// 初始化
xhr.open(method, url, async)
// 发送请求
xhr.send(data)
// 设置状态变化回调处理请求结果
xhr.onreadystatechange = () => {
  if (xhr.readyState === 4 && xhr.status === 200) {
    console.log(xhr.responseText)
  }
}

// 2. 基于promise实现

function ajax (options) {
  // 请求地址
  const url = options.url
  // 请求方法
  const method = options.method.toLocaleLowerCase() || 'get'
  // 默认为异步true
  const async = options.async
  // 请求参数
  const data = options.data
  // 实例化
  const xhr = new XMLHttpRequest()
  // 请求超时
  if (options.timeout && options.timeout > 0) {
    xhr.timeout = options.timeout
  }
  // 返回一个Promise实例
  return new Promise ((resolve, reject) => {
    xhr.ontimeout = () => reject && reject('请求超时')
    // 监听状态变化回调
    xhr.onreadystatechange = () => {
      if (xhr.readyState == 4) {
        // 200-300 之间表示请求成功,304资源未变,取缓存
        if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
          resolve && resolve(xhr.responseText)
        } else {
          reject && reject()
        }
      }
    }
    // 错误回调
    xhr.onerror = err => reject && reject(err)
    let paramArr = []
    let encodeData
    // 处理请求参数
    if (data instanceof Object) {
      for (let key in data) {
        // 参数拼接需要通过 encodeURIComponent 进行编码
        paramArr.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
      }
      encodeData = paramArr.join('&')
    }
    // get请求拼接参数
    if (method === 'get') {
      // 检测url中是否已存在 ? 及其位置
      const index = url.indexOf('?')
      if (index === -1) url += '?'
      else if (index !== url.length -1) url += '&'
      // 拼接url
      url += encodeData
    }
    // 初始化
    xhr.open(method, url, async)
    // 发送请求
    if (method === 'get') xhr.send(null)
    else {
      // post 方式需要设置请求头
      xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded;charset=UTF-8')
      xhr.send(encodeData)
    }
  })
}

http、浏览器

一、浏览器存储的方式有哪些

特性 cookie localStorage sessionStorage indexedDB
数据生命周期 一般由服务器生成,可以设置过期时间 除非被清理,否则一直存在 页面关闭就清理 除非被清理,否则一直存在
数据存储大小 4K 5M 5M 无限
与服务端通信 每次都会携带在 header,中,对于请求性能影响 不参与 不参与 不参与

补充:cookie 原本并不是用来储存的,而是用来与服务端通信的,需要存取请自行封装 api。
而 localStorage 则自带 getItem 和 setItem 方法,使用很方便。

localStorage 注意点:

  1. localStorage 只能存字符串,存取 JSON 数据需配合 JSON.stringify() 和 JSON.parse()
  2. 遇上禁用 setItem 的浏览器,需要使用 try...catch 捕获异常

二、输入URL发生什么?

  1. DNS 域名解析(域名解析成ip地址,走UTP协议,因此不会有握手过程):浏览器将 URL 解析出相对应的服务器的 IP 地址(1. 本地浏览器的 DNS 缓存中查找 2. 再向系统DNS缓存发送查询请求 3. 再向路由器DNS缓存 4. 网络运营商DNS缓存 5. 递归搜索),并从 url 中解析出端口号
  2. 浏览器与目标服务器建立一条 TCP 连接(三次握手)
  3. 浏览器向服务器发送一条 HTTP 请求报文
  4. 服务器返回给浏览器一条 HTTP 响应报文
  5. 浏览器进行渲染
  6. 关闭 TCP 连接(四次挥手)

三、浏览器渲染的步骤

  1. HTML 解析出 DOM Tree
  2. CSS 解析出 Style Rules
  3. 两者关联生成 Render Tree
  4. Layout(布局)根据 Render Tree 计算每个节点的信息
  5. Painting 根据计算好的信息进行渲染整个页面

浏览器解析文档的过程中,如果遇到 script 标签,会立即解析脚本,停止解析文档(因为 JS 可能会改变 DOM 和 CSS,如果继续解析会造成浪费)。
如果是外部 script, 会等待脚本下载完成之后在继续解析文档。现在 script 标签增加了 defer 和 async 属性,脚本解析会将脚本中改变 DOM 和 css 的地方> 解析出来,追加到 DOM Tree 和 Style Rules 上

四、页面渲染优化

基于对渲染过程的了解,推荐一下优化:

  1. HTML 文档结构层次尽量少,最好不深于 6 层
  2. 脚本尽量放后边,避免组织页面加载
  3. 少量首屏样式可以放在便签内
  4. 样式结构层次尽量简单
  5. 脚本减少 DOM 操作,减少回流,尽量缓存访问 DOM 的样式信息
  6. 尽量减少 JS 修改样式,可以通过修改 class 名的方式解决
  7. 减少 DOM 查找,缓存 DOM 查找结果
  8. 动画在屏幕外或页面滚动时,尽量停止

五、强制缓存和协商缓存

  • 强制缓存是我们在第一次请求资源时在 http 响应头设置一个过期时间,在时效内都将直接从浏览器进行获取,常见的 http 响应头字段如 Cache-Control 和 Expires

  • 协商缓存是我们通过 http 响应头字段 etag 或者 Last-Modified 等判断服务器上资源是否修改,如果修改则从服务器重新获取,如果未修改则 304 指向浏览器缓存中进行获取

六、GET 和 POST 请求的区别

  • GET 参数通过 url 传递,POST 放在 body 中。(http 协议规定,url 在请求头中,所以大小限制很小)
  • GET 请求在 url 中传递的参数是有长度限制的,而 POST 没有。原因见上↑↑↑
  • GET 在浏览器回退时是无害的,而 POST 会再次提交请求
  • GET 请求会被浏览器主动 cache,而 POST 不会,除非手动设置
  • GET 比 POST 更不安全,因为参数直接暴露在 url 中,所以不能用来传递敏感信息
  • 对参数的数据类型,GET 只接受 ASCII字符,而 POST 没有限制
  • GET 请求只能进行 url(x-www-form-urlencoded)编码,而 POST 支持多种编码方式
  • GET 产生一个 TCP 数据包;POST 产生两个 TCP 数据包。对于 GET 方式的请求,浏览器会把 http 的 header 和 data 一并发送出去,服务器响应200(返回数据)。而对于 POST,浏览器先发送 header,服务器响应100 continue,浏览器再发送 data,服务器响应200 ok(返回数据)

七、HTTP1.0 / 1.1 / 2.0 及HTTPS

你需要知道的HTTP常识
可能是全网最全的http面试答案
如何优雅的谈论HTTP/1.0/1.1/2.0

HTTP1.1 是当前使用最为广泛的HTTP协议

  • HTTP1.0 和 HTTP1.1 相比
    • HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD 方法。HTTP1.1 新增了六种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法。
    • 缓存处理:在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。
    • 带宽优化及网络连接的使用:HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
    • 错误通知的管理:在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
    • Host头处理:在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。
    • 长连接:HTTP 1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection: keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。通过设置http的请求头部和应答头部,保证本次数据请求结束之后,下一次请求仍可以重用这一通道,避免重新握手。
  • HTTP2.0 和 HTTP1.X 相比
    • 新的二进制格式(Binary Format):HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮。
    • 多路复用(MultiPlexing):即连接共享,即每一个request都是是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里面。
    • header压缩:如上文中所言,对前面提到过HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用了专门为首部压缩而设计的 HPACK 算法,使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。
    • 服务端推送(server push):服务端推送能把客户端所需要的资源伴随着index.html一起发送到客户端,省去了客户端重复请求的步骤。正因为没有发起请求,建立连接等操作,所以静态资源通过服务端推送的方式可以极大地提升速度。例如我的网页有一个sytle.css的请求,在客户端收到sytle.css数据的同时,服务端会将sytle.js的文件推送给客户端,当客户端再次尝试获取sytle.js时就可以直接从缓存中获取到,不用再发请求了。
  • HTTPS 与 HTTP 相比
    • HTTPS协议需要到CA申请证书,一般免费证书很少,需要交费。
    • HTTP协议运行在TCP之上,所有传输的内容都是明文,HTTPS运行在SSL/TLS之上,SSL/TLS运行在TCP之上,所有传输的内容都经过加密的。
    • HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
    • HTTPS可以有效的防止运营商劫持,解决了防劫持的一个大问题。

HTTPS 介绍:HTTPS在传输数据之前需要客户端(浏览器)与服务端(网站)之间进行一次握手,在握手过程中将确立双方加密传输数据的密码信息。TLS/SSL协议不仅仅是一套加密传输的协议,TLS/SSL中使用了非对称加密,对称加密以及HASH算法。

握手过程的简单描述如下:

1.浏览器将自己支持的一套加密规则发送给网站。
2.网站从中选出一组加密算法与HASH算法,并将自己的身份信息以证书的形式发回给浏览器。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息。
3.获得网站证书之后浏览器要做以下工作:
      a. 验证证书的合法性(颁发证书的机构是否合法,证书中包含的网站地址是否与正在访问的地址一致等),如果证书受信任,则浏览器栏里面会显示一个小锁头,否则会给出证书不受信的提示。
      b. 如果证书受信任,或者是用户接受了不受信的证书,浏览器会生成一串随机数的密码,并用证书中提供的公钥加密。
      c. 使用约定好的HASH计算握手消息,并使用生成的随机数对消息进行加密,最后将之前生成的所有信息发送给网站。
4.网站接收浏览器发来的数据之后要做以下的操作:
      a. 使用自己的私钥将信息解密取出密码,使用密码解密浏览器发来的握手消息,并验证HASH是否与浏览器发来的一致。
      b. 使用密码加密一段握手消息,发送给浏览器。
5.浏览器解密并计算握手消息的HASH,如果与服务端发来的HASH一致,此时握手过程结束,之后所有的通信数据将由之前浏览器生成的随机密码并利用对称加密算法进行加密。

这里浏览器与网站互相发送加密的握手消息并验证,目的是为了保证双方都获得了一致的密码,并且可以正常的加密解密数据。其中非对称加密算法用于在握手过程中加密生成的密码,对称加密算法用于对真正传输的数据进行加密,而HASH算法用于验证数据的完整性。由于浏览器生成的密码是整个数据加密的关键,因此在传输的时候使用了非对称加密算法对其加密。非对称加密算法会生成公钥和私钥,公钥只能用于加密数据,因此可以随意传输,而网站的私钥用于对数据进行解密,所以网站都会非常小心的保管自己的私钥,防止泄漏。TLS握手过程中如果有任何错误,都会使加密连接断开,从而阻止了隐私信息的传输。正是由于HTTPS非常的安全,攻击者无法从中找到下手的地方,于是更多的是采用了假证书的手法来欺骗客户端,从而获取明文的信息。

我总结的前端面试题_第1张图片

八、介绍下304过程

a. 浏览器请求资源时首先命中资源的Expires 和 Cache-Control,Expires 受限于本地时间,如果修改了本地时间,可能会造成缓存失效,可以通过Cache-control: max-age指定最大生命周期,状态仍然返回200,但不会请求数据,在浏览器中能明显看到from cache字样。

b. 强缓存失效,进入协商缓存阶段,首先验证ETagETag可以保证每一个资源是唯一的,资源变化都会导致ETag变化。服务器根据客户端上送的If-None-Match值来判断是否命中缓存。

c. 协商缓存Last-Modify/If-Modify-Since阶段,客户端第一次请求资源时,服务服返回的header中会加上Last-Modify,Last-modify是一个时间标识该资源的最后修改时间。再次请求该资源时,request的请求头中会包含If-Modify-Since,该值为缓存之前返回的Last-Modify。服务器收到If-Modify-Since后,根据资源的最后修改时间判断是否命中缓存。

我总结的前端面试题_第2张图片

九、HTTP 状态码

  • 1xx(临时响应)表示临时响应并需要请求者继续执行操作的状态码
    • 100 - 继续 请求者应当继续提出请求。服务器返回此代码表示已收到请求的第一部分,正在等待其余部分
    • 101 - 切换协议 请求者已要求服务器切换协议,服务器已确认并准备切换
  • 2xx(成功)表示成功处理了请求的状态码
    • 200 - 成功 服务器已经成功处理了请求。通常,这表示服务器提供了请求的网页
    • 201 - 已创建 请求成功并且服务器创建了新的资源
    • 202 - 已接受 服务器已接受请求,但尚未处理
    • 203 - 非授权信息 服务器已经成功处理了请求,但返回的信息可能来自另一来源
    • 204 - 无内容 服务器成功处理了请求,但没有返回任何内容
    • 205 - 重置内容 服务器成功处理了请求,但没有返回任何内容
    • 206 - 部分内容 服务器成功处理了部分GET请求
  • 3xx(重定向)表示要完成请求,需要进一步操作;通常,这些状态代码用来重定向
    • 300 - 多种选择 针对请求,服务器可执行多种操作。服务器可根据请求者(user agent)选择一项操作,或提供操作列表供请求者选择
    • 301 - 永久移动 请求的网页已永久移动到新位置。服务器返回此响应(对GET或HEAD请求的响应)时,会自动将请求者转到新位置
    • 302 - 临时移动 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求
    • 303 - 查看其它位置 请求者应当对不同的位置使用单独的GET请求来检索响应时,服务器返回此代码
    • 304 - 未修改 自上次请求后,请求的网页未修改过。服务器返回此响应,不会返回网页的内容
    • 305 - 使用代理 请求者只能使用代理访问请求的网页。如果服务器返回此响应,还表示请求者应使用代理
    • 307 - 临时性重定向 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有的位置来进行以后的请求
  • 4xx(请求错误)这些状态码表示请求可能出错,妨碍了服务器的处理
    • 400 - 错误请求 服务器不理解请求的语法
    • 401 - 未授权 请求要求身份验证。对于需要登录的网页,服务器可能返回此响应
    • 403 - 禁止 服务器拒绝请求
    • 404 - 未找到 服务器找不到请求的网页
    • 405 - 方法禁用 禁用请求中指定的方法
    • 406 - 不接受 无法使用请求的内容特性响应请求的网页
    • 407 - 需要代理授权 此状态码与401(未授权)类似,但指定请求者应当授权使用代理
    • 408 - 请求超时 服务器等候请求时发生超时
    • 409 - 冲突 服务器在完成请求时发生冲突。服务器必须在响应中包含有关冲突的信息
    • 410 - 已删除 如果请求的资源已永久删除,服务器就会返回此响应
    • 411 - 需要有效长度 服务器不接受不含有效内容长度标头字段的请求
    • 412 - 未满足前提条件 服务器未满足请求者在请求者设置的其中一个前提条件
    • 413 - 请求实体过大 服务器无法处理请求,因为请求实体过大,超出了服务器的处理能力
    • 414 - 请求的URI过长 请求的URI(通常为网址)过长,服务器无法处理
    • 415 - 不支持媒体类型 请求的格式不受请求页面的支持
    • 416 - 请求范围不符合要求 如果页面无法提供请求的范围,则服务器会返回此状态码
    • 417 - 未满足期望值 服务器未满足“期望”请求标头字段的要求
  • 5xx(服务器错误)这些状态码表示服务器在尝试处理请求时发生内部错误。这些错误可能是服务器本身的错误,而不是请求出错
    • 500 - 服务器内部错误 服务器遇到错误,无法完成请求
    • 501 - 尚未实施 服务器不具备完成请求的功能。例如,服务器无法识别请求方法时可能会返回此代码
    • 502 - 错误网关 服务器作为网关或代理,从上游服务器无法收到无效响应
    • 503 - 服务器不可用 服务器目前无法使用(由于超载或者停机维护)。通常,这只是暂时状态
    • 504 - 网关超时 服务器作为网关代理,但是没有及时从上游服务器收到请求
    • 505 - HTTP版本不受支持 服务器不支持请求中所用的HTTP协议版本

 十、对前后端跨域可以说一下吗?如何解决跨域的?

九种跨域方式实现原理
如何跨域我们已经明白了,如果这样问:浏览器没有同源策略会有什么危险?是不是有点瞬间懵逼?
下面是摘选的事例,参考:AJAX跨域访问被禁止的原因

假设有一个黑客叫做小黑,他从网上抓取了一堆美女图做了一个网站,每日访问量爆表。
为了维护网站运行,小黑挂了一张收款码,觉得网站不错的可以适当资助一点,可是无奈伸手党太多,小黑的网站入不敷出。
于是他非常生气的在网页中写了一段js代码,使用ajax向淘宝发起登陆请求,因为很多数人都访问过淘宝,所以电脑中存有淘宝的cookie,不需要输入账号密码直接就自动登录了,然后小黑在ajax回调函数中解析了淘宝返回的数据,得到了很多人的隐私信息,转手一卖,小黑的网站终于盈利了。
如果跨域也可以发送AJAX请求的话,小黑就真的获取到了用户的隐私并成功获利了!!!

Vue

简述MVVM

后续会持续更新。
 

你可能感兴趣的:(前端,面试,vue.js,css,html)