前端面试题梳理

准备面试,个人梳理,比较乱;

如何实现图片懒加载

  • 利用自定义属性data-imageUrl先存放图片地址,监听外层盒子滚动事件,遍历检测图片位置,若在可视区内,将data-imageUrl赋值给src显示图片;

    可视区内:

    方法1

    const bound = el.getBoundingClientRect() 返回一个位置对象

    const clientHeight = window.innerHeight 可视区高度

    bound.top <= clientHeight + 50 时在可视区内 (50是为了提前加载图片)

    方法2

    1. 通过document.documentElement.clientHeight获取屏幕可视窗口高度

    2. 通过element.offsetTop获取元素相对于文档顶部的距离

    3. 通过document.documentElement.scrollTop获取浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离

      2-3 >0则在可视区内

  • 优化:

    1. 滚动时不断遍历和赋值src,损耗性能,可以利用节流函数,在用户滚动某一个时间后才遍历和赋值。(效果看起来就像是用户停止滚动时加载图片)
    2. 设一个标识记录已加载图片的index,当滚动时就不遍历所有图片了。

前端性能优化

  • 对高频触发的事件进行节流或消抖(debouncethrottle 这两个函数),scroll事件及tochmove等高频事件;

  • css避免使用通配符*(它会匹配所有元素)

  • js放底部css放顶部(js会阻塞页面解析,css会阻塞页面呈现和js执行),但如今使用webpack等工具基本上不用考虑这种影响;

  • 首屏: 减少网络请求次数,减小文件体积使用cdn加速(gulp、webpack、grunt等构件工具压缩合并)

    以上参考链接:https://mp.weixin.qq.com/s/qglFD2nHFqFBivb8T23Qtg

  • 浏览器缓存:服务器响应的header 信息

缓存策略 获取资源形式 状态码 发送请求到服务器
强缓存 从缓存取 200(from memory cache) 否,直接从缓存取
协商缓存 从缓存取 304(not modified) 是,通过服务器来告知缓存是否可用

Service worker 可以解决目前离线应用的问题,同时也可以做更多的事。 Service Worker 可以使你的应用先访问本地缓存资源,所以在离线状态时,在没有通过网络接收到更多的数据前,仍可以提供基本的功能(一般称之为 Offline First)。这是原生APP 本来就支持的功能,这也是相比于 web app ,原生 app 更受青睐的主要原因。

参考链接:https://juejin.im/post/5b0bff30f265da08f76cc6f0

  • 如果需要动态改变css样式 尽量使用改变className 只触发一次reflow

  • 慎用全局变量

    • 全局变量需要搜索更长的作用域链。
    • 全局变量的生命周期比局部变量长,不利于内存释放。
  • 类型转换隐式转换要比api转换效率高

  • 使用事件代理(事件委托):当要对多个元素绑定事件时可以加将事件绑定到父元素然后通过判断e.target.nodeName执行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <ul id="parent-list">
    <li id="post-1">Item 1
    <li id="post-2">Item 2
    <li id="post-3">Item 3
    <li id="post-4">Item 4
    <li id="post-5">Item 5
    <li id="post-6">Item 6
    </li></ul>
    // Get the element, add a click listener...
    document.getElementById("parent-list").addEventListener("click",function(e) {
    // e.target is the clicked element!
    // If it was a list item
    if(e.target && e.target.nodeName == "LI") {
    // List item found! Output the ID!
    console.log("List item ",e.target.id.replace("post-")," was clicked!");
    }
    });

以上参考链接: https://segmentfault.com/a/1190000003646305

  • 延迟加载 非首屏使用的数据样式脚本图片等,用户交互时才显示。(例如vue的路由懒加载)
  • 减少iframe的使用:阻塞页面,缺乏语义;
  • 首选get请求 post有两步先发送header再发送数据,而get只有一个步骤,tcp数据包发送数据;
  • 减少cookie大小,cookie通过http投在服务器和浏览器间来回传送,减小可降低响应速度。
  • 使用 DocumentFragment 暂存 DOM,整理好以后再插入 DOM 树

以上参考链接: https://csspod.com/frontend-performance-best-practices/

什么是虚拟 DOM,为什么说虚拟dom比真实dom快

  • 我的理解是 Virtual DOM也就是虚拟dom,其实是js对象模拟的dom树
  • js操作数据比真实dom要快很多
  • 处理完以后再渲染成真实dom

谈一下你对 MVVM 的认识

  • 首先说一下mvm:将软件分为三部分

    • 视图(view): 用户界面
    • 控制器(controller): 业务逻辑
    • 模型(model): 数据保存

    他们之间的通信,view传达指令给controller,controller完成业务逻辑要求Model改变状态,Model将新的数据发送到view,用户得到反馈。单向

  • MVVM 模式将controller改名为viewModel

    区别就是它采用双向数据绑定,view的变动自动反映在viewModel上,反之亦然 viewModel负责把model的数据同步到view显示出来,还负责把view的修改同步会model

写一个函数把url参数以键值对的方式放到一个对象里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 let url = 'https://www.baid.com/s?edw=ddds&ksjn54=546&sdcnsl=5465&cs=54'
// 字符串切割处理
function params(url) {
if(url.indexOf('?') < 0 ) return
let arr = url.split('?')
let arrL = arr[1].split('&')
let obj = {}
for(let i=0;i<arrL.length;i++){
let cur = arrL[i].split('=')
obj[cur[0]] = cur[1]
}
return obj
}
console.log(params(url))
// {edw: "ddds", ksjn54: "546", sdcnsl: "5465", cs: "54"}

// 正则
function paramsA(url){
let reg = /([^&?=]+)=([^&?=]+)/g
let obj = {}
url.replace(reg, function(){
console.log(arguments)
obj[arguments[1]] = arguments[2]
})
return obj
}

数组去重

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 利用indexOf返回第一个符合要求的index   
[256, 22, '11', 33, 22, 66, '11', '11'].filter((v, i, array) => {
return array.indexOf(v) === i
}) // [256, 22, "11", 33, 66]
// 利用Set的唯一性
let arr = [1,3,2,1,'1',3]
let setArr = [...new Set(arr)] //  [1, 3, 2, "1"]

// 利用对象属性唯一
let arr = [256, 22, 33, 22, 66, '11', '11']
function arrS(arr) {
let obj = {}
let result = []
for(let i=0;i<arr.length ;i++){
if(!obj[arr[i]]) {
obj[arr[i]] = i
result.push(arr[i])
}
}
return result
}
arrS(arr) // [256, 22, 33, 66, "11"]

取数组中最大值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let arr = [1321,515,6,565,151,-85,55]
// 扩展运算符
Math.max(...arr)
// apply
Math.max.apply(null, arr)
// for循环 每次循环取出较大的一个和后面一个对比 循环结束最终就是最大的
let max = arr[0]
for(let i = 0; i < arr.length; i++) {
max = max < arr[i+1] ? arr[i+1] : max
}
// sort 降序排序取第一个
arr.sort((num1, num2)) {
return num2 - num1
}
arr[0]
// reduce
arr.reduce((num1, num2) => {
return num1 > num2 ? num1 : num2}
)
// 这个方法类似于上面的for循环 不断对比返回较大的值

闭包的作用,优缺点

  • 形成私有作用域保护私有变量不受外界干扰;
  • 形成不销毁的栈内存,保存起来;(jq)
  • 缺点:耗内存

三栏布局 左右300px高固定中间自适应

  • 浮动:左右各左右浮动宽度300px;
  • 定位:外层容器相对定位,左中右绝对定位,左left0top0,中left300right300右right0top0;
  • flex布局:外层容器display:flex;左右给width:300px;中flex:1;自适应
  • table布局:外层容器display:table;width:100%;height固定,左中右display:table-cell;左右宽度固定;
  • grid布局(网格);外层容器display:grid;witdth:100%;grid-template-rows:100px;gridtemplate-cloums:300px auto 300px;

CSS盒模型

  • 基本概念:标准模型+IE模型
    • 标准模型的宽高就是content的宽高
    • IE模型宽高包含padding和border
  • 设置两种模型
    • box-sizing: content-box;标准模型,浏览器默认
    • box-sizing:border-box;IE模型
  • 获取盒模型对应的宽和高
    • dom.style.width/height只能取到内联样式
    • dom.currentStyle.width/heigt 最终渲染后的宽高(仅支持IE)
    • window.getComputedStyle(dom).width/height 最终渲染后的宽高(通用性更好一些)
    • dom.getBoundingClientRect().width/height 最终渲染的宽和高

BFC 边距重叠解决方案

  • 基本概念:块级格式化上下文;
  • 原理(渲染规则)
    • BFC元素垂直方向边距会重叠
    • BFC的区域不会与浮动元素的box重叠(清除浮动)
    • BFC在页面上是独立的容器不会与外面的元素互相影响
    • 计算BFC高度时浮动元素也会参与计算
  • 如何创建BFC
    • overflow的值不为”visible
    • float不为none;
    • position值不为static 或 relative;
    • display:table-cell”, “table-caption”, or “inline-block”中的任何一个;
  • 解决边距问题
  • 清除浮动对页面的影响
  • 解决父元素没有被脱离标准流的子元素撑高为0

DOM事件相关

  • DOM事件类-事件级别
    • DOM0 element.onclick = function(){}
    • DOM2 element.addEventListener(‘click’,function(){},false)
    • DOM3 element.addEventListener(‘keyup’,function(){},false)
  • 事件模型
    • 冒泡
    • 捕获
  • 事件流
    • 事件通过捕获到达目标元素,目标元素再上传到window
    • 事件捕获具体流程 window=>document=>html=>body=>…=>目标元素
  • Event对象的常见应用
    • event.preventDefault() 阻止默认行为
    • event.stopPropagation() 阻止冒泡
    • event.stoplmmediatePropagation() 事件优先级
    • event.currentTarget 当前绑定事件的元素
    • event.target 当前被点击的元素
  • 自定义事件
1
2
3
4
5
var eve = new Event('custome')
ev.addEventListener('custome',function(){
console.log('custome')
})
ev.dispatchEvent(eve)

HTTP协议

  • http协议的主要特点:简单快速,灵活,无连接,无状态;

  • 报文组成部分

    • 请求报文:请求行(http方法,http协议,页面地址),请求头(key-value格式),空行(服务器区分请求头和请求体),请求体;
    • 状态行,响应头,空行,响应体;
  • http方法

    • GET 获取资源
    • POST 传输资源
    • PUT 更新资源
    • DELETE 删除资源
    • HEAD 获得报文首部
  • GET和POST的区别

    • GET请求在URL传递参数是有长度限制的,post没有
    • GET只接收ASCII字符而POST没有限制
    • GET请求通过URL传递参数POST放在Request body中
    • 相对来说GET更不安全一些,不能传递敏感信息
    • GET请求浏览器主动缓存,POST不会
  • 状态码

    • 1XX :指示信息-表示请求已接收,继续处理

    • 2XX:成功-表示请求已被成功接收

    • 3XX:重定向-要完成请求必须进行更进一步操作

    • 4XX:客户端错误-请求有语法错误或请求无法实现

    • 5XX:服务器错误-服务器未能实现合法的请求

    • 常见的状态码:200 客户端请求成功,206客户发送了一个带有Range头的get请求,服务器完成了他,301所有请求的页面已经转移至新的url,302所请求页面已经临时转移新url304 客户端有缓存的文档并发出了一个条件性请求,服务器告诉客户端,原来缓存的文档还可以继续使用。400 客户端请求语法错误不能被服务器理解;401请求未经授权 403访问被禁止404请求不资源不存在500服务器发生不可预期错误503请求未完成服务器临时过载或当机一段时间后可能恢复

  • 持久连接 keep-alive http版本1.1支持

    • 非keep-alive模式,每个请求/应答客户和服务器都要新建立一个连接,完成之后立即断开连接(http协议为无连接的协议)
    • 使用keep-alive模式时,keep-Alive功能使客户端到服务器端连接持续有效,当出现服务器的后续请求时,keep-alive功能避免了建立或者重新建立连接。

创建对象的几种方法

1
2
3
4
5
6
7
8
9
10
11
12
// 字面量
let obj = {a:1}
// new运算符
let obj1 = new Object({b:2)
// 构造函数
let Fn = function(num) {
this.c = num
}
let CC = new Fn(3)
// Object.create
let DD = {d: 4}
let D4 = Object.create(DD)//以DD为原型的对象

原型、构造函数、实例

  • 实例通过new构造函数创建
  • 构造函数有prototype属性指向原型对象
  • 原型对象constructor指向构造函数(构造器)
  • 构造函数.prototype.constructor === 构造函数
  • 实例.__proto_指向原型对象
  • instanceof 可以判断是否在原型链上 constructor更加严谨

new运算符过程

  • 例如 let foo2 = new Foo()
    • 一个新对象被创建。它继承来自于foo.prototype
    • 构造函数foo被执行,执行的时候相应的传参会被传入,同时上下文(this),会被指定为这个新实例。
    • 如果构造函数返回了一个对象,那么这个对象会取代整个new出来的结果。如果构造函数没有返回对象,那么new出来的结果为步骤1创建的对象。

面向对象

  • 类的声明

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 构造函数
    function Person(name) {
    thus.name = name
    }
    console.log(new Person('holly'))
    // ES6 class
    class Person1() {
    constructor(name) {
    this.name = name
    }
    }
    console.log(new Person1('holly'))
  • 类的继承

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    // <-- 借助构造函数实现继承 call -->
    function Person() {
    thus.name = 'person'
    }
    function Child1() {
    Person1.call(this)
    // 只能继承Person的私有属性
    this.type = 'child'
    }
    // <-- 原型链继承 -->
    function Person2() {
    this.name = 'parent2'
    this.play = [1,2,3]
    }
    function Child2() {
    this.type = 'child2'
    }
    // child2的原型对象指向Person2的实例 实现私有公有继承
    Child2.prototype = new Person2()
    // 缺点 改变一个实例的远行对象另一个也会变
    let s1 = new Child2()
    let s2 = new Child2()
    s1.play.push(4)
    console.log(s1.play) // [1,2,3,4]
    console.log(s2.play) // [1,2,3,4]
    // 改变一个实例的原型对象另一个也会改变

    // <-- 组合方式 -->
    function Person3 () {
    this.name = 'person3'
    this.play = [1,2,3]
    }
    function Child3() {
    Person3.call(this)
    this.name = 'child3'
    }
    Child3.prototype = new Person3()
    let s3 = new Child3()
    let s4 = new Child3()
    s1.play.push(4)
    console.log(s3.play) // [1,2,3,4]
    console.log(s4.play) // [1,2,3]
    //缺点 执行了两次父类 constructor也继承了
    // <-- 组合的优化 -->
    function Person3 () {
    this.name = 'person3'
    this.play = [1,2,3]
    }
    function Child3() {
    Person3.call(this) // 拿到私有属性
    this.name = 'child3'
    }
    Child3.prototype = Parent3.prototype //拿到原型指向
    let s3 = new Child3()
    let s4 = new Child3()
    s1.play.push(4)
    console.log(s3.play) // [1,2,3,4]
    console.log(s4.play) // [1,2,3]
    //缺点 constructor也继承了

    // <-- 优化2 -- >
    Child3.prototype = Parent3.prototype //改为下行
    Child3.prototype = Object.create(Parent3.prototype)// 可以实现一层一层原型

完全

  • CSRF:跨站请求伪造(用户在A登陆过,B引诱用户点击A接口)
    • 可以用token验证
    • referer验证
    • 隐藏令牌
  • XSS : 跨域脚本攻击,向页面注入js
    • 避免的宗旨是让注入的js无法执行

算法

  • 排序

    • 快速排序:取中间值左右两边放大于和小于然后递归实现

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      let quickSort = function(arr){
      if(arr.length <= 1) {return arr}
      let pivotIndex = Math.floor(arr.length / 2)
      // 定义一个基准 数组中间的那个数
      var pivot = arr.splice(pivotIndex, 1)[0]
      var left = []
      var right = []
      // 循环数组 小于基准的放left 大于等于基准的放right
      for(let i = 0; i < arr.length; i++) {
      if(arr[i] < pivot) {
      left.push(arr[i])
      } else {
      right.push(arr[i])
      }
      }
      // 递归这个过程
      return quickSort(left).concat([pivot], quickSort(right))
      }

    • 选择排序

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      function selectionSort(arr) {
      var len = arr.length;
      var minIndex, temp;
      for (var i = 0; i < len - 1; i++) {
      minIndex = i;
      for (var j = i + 1; j < len; j++) {
      if (arr[j] < arr[minIndex]) { // 寻找最小的数
      minIndex = j; // 将最小数的索引保存
      }
      }
      temp = arr[i];
      arr[i] = arr[minIndex];
      arr[minIndex] = temp;
      }
      return arr;
      }

    • 希尔排序

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      function shellSort(arr) {
      var len = arr.length,
      temp,
      gap = 1;
      while(gap < len/3) { //动态定义间隔序列
      gap = gap*3+1;
      }
      for (gap; gap > 0; gap = Math.floor(gap/3)) {
      for (var i = gap; i < len; i++) {
      temp = arr[i];
      for (var j = i-gap; j >= 0 && arr[j] > temp; j -= gap) {
      arr[j+gap] = arr[j];
      }
      arr[j+gap] = temp;
      }
      }
      return arr;
      }

    • 冒泡排序:两两对比需要循环两层,内层做次数优化

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      function bubbleSort(arr){
      let len = arr.length
      for(let i = 0; i< len; i++) {
      for(let j = 0; j < len -1 -i; j++) {
      if(arr[j] > arr[j+1]){
      let temp = arr[j+1]
      arr[j+1] = arr[j]
      arr[j] = temp
      }
      }
      }
      return arr
      }

数组的常用方法

  • concat: 链接两个或多个数据,返回结果;

  • every: 对数组中每一个项运行给定的函数,如果该函数每一项都返回true,则返回true;

  • some:对数组中每一项运行给定函数,如果任一项返回true,则返回true;

  • map: 对数组中每一项运行给定函数,返回每次函数调用的结果组成的数组;

  • filter: 对数组中的每一项运行给定函数,然后该函数返回true的项组成的数组;

  • forEach: 对数组中每一项运行给定函数,没有返回值;

  • join: 将所有的数组元素链接成一个字符串可以给定连接符(‘-’),默认逗号;

  • indexOf:返回第一个与给定参数相等的数组元素的索引,没有找到返回-1;

  • lastIndexOf: 返回在数组中搜索到与给定参数相等的元素的索引里最大的值;

  • reverse:颠倒数组中元素的顺序;

  • slice: 传入索引值,将数组中对应索引范围内的元素作为新元素返回,不改变原数组;

  • splice: 传入从索引A开始 删除几个(b)替换为(c);

  • toString: 将数组作为字符串返回;

  • valueOf 和 toString相似,将数组作为字符串返回;

  • reduce() 可以实现累加

    [1,2,3,4,5].reduce(() => {return a + b})

  • includes: 传入参数在数组中返回true

  • sort:传入函数参数为a,b 函数体return a-b(升序)b-a(降序)

  • push: 向数组最后插入,返回数组长度;

  • pop: 传入从后删除几项,返回被删除的项,默认一项;

  • shift:传入从前删除几项,返回被删除的项,默认一项;

  • unshift向前插入,返回数组长度;

字符串常用方法

  • charAt(n): 返回字符串第n个字符,如果n不在0-字符串.length上返回空字符串;

  • indexOf(a);返回a在字符串中首次出现的索引,没有返回-1;

  • lastIndexOf():搜索字符在字符串中最后出现的索引,没有返回-1;

  • substring(a,b):返回a-b之间的字符串,不改变原字符串;(包头不包尾)

  • slice(a,b):返回a-b之间的字符串,不改变原字符串(包头不包尾)

  • replace(a,b) ;b替换a返回新字符串,不改变元字符串

  • match(regexp)返回一个包含匹配结果的数组,如果没有匹配项返回null

  • trim()去除字符串开头结尾的空白符,返回str副本不影响原字符串;

  • tolowerCase()转为小写

  • toUpperCase()转为大写

  • cancat()链接字符串

  • split把字符串分割为以传入为分隔的数组

Math常用方法

  • ceil()向上取整
  • floor向下取整
  • round四舍五入
  • max(x,y)返回最大值
  • min(x,y)返回最小值
  • pow(x,y)返回x的y次幂
  • random() 返回0~1之间的随机数
  • sqrt()返回数的平方根

正则

  • compile编译正则表达式
  • exec 检索字符串中指定值返回找到的值并确定位置
  • test检索字符串中指定的值,返回true或false
  • search检索正则表达式相匹配的值
  • match找到一个或多个正则表达式匹配
  • raplace替换与正则表达式匹配的字符串

对象

  • Object.assign({}, person),将一个或多个对象中的值复制到一个目标对象
  • Object.keys(obj) 返回obj属性组成的数组
  • Object.defineProperty() 修改或创建对象属性的特性
  • a.hasOwnProperty(b) a是否有属性b 返回boolean

DOCTYPE作用

  • 声明文档类型和DTD规范,主要用途:文件合法性验证;
    • htm5写法 <!DOCTYPE html>

浏览器渲染机制

  • 渲染过程 html从上至下渲染domtree

  • cssom tree

  • domtree cssomtree 整合成rendertree

重排reflow

  • 增加删除修改dom
  • 移动dom位置
  • 修改css样式
  • resize窗口的时候,或者滚动
  • 修改网页默认字体时

js运行机制

  • js是单线程的
  • 任务队列
  • Event Loop 事件循环
  • 所有同步任务都在主线程上执行,形成一个执行毡
  • 主线程之外还存在任务队列,只要是异步任务,就在任务队列中
  • 一旦所有同步任务执行完毕,系统会读取任务队列,开始执行
  • 主线程不断重复上面三部

页面性能

  • 资源压缩合并,减少http请求;
  • 异步加载
    • 动态加载
    • defer html解析玩后执行按顺序
    • async加载完之后执行,不按顺序
  • 浏览器缓存
    • 强缓存
      • expires:服务器过期时间绝对时间
      • Cache-Contro; max-age=3600相对时间 单位(秒)
    • 协商缓存 问服务器是否缓存
  • CDN
  • 预解析DNS

错误监控

  • 错误分类

    • 代码错误
    • 资源加载失败
  • 错误捕获方式

    • try…catch

html5新标签

  • header/footer
  • section/article
  • nav
  • aside
------ 本文结束------
0%