Skip to content

前端面经【八股文合集】

javaScript基础

说一说javascript的数据类型

基本数据类型:string,number,boolean,null,undefined

  • String: 文本字符串.
  • Number: 整数和浮点数.
  • Boolean: 表示 真 (true) / 伪 (false) 的两个特殊值.
  • Null: 表示空值.
  • Undefined: 表示未定义或者不存在.
  • Symbol: 表示独一无二的值, 可以保证不会与其他属性名产生冲突.
  • BigInt: 能够支持比 Number 类型的范围更大的整数值类型.

引用数据类型:object

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

  • Array.isArray(arr)
  • arr instanceof Array
  • Object.prototype.toString.call(arr) === '[object Array]'
  • 对象原型(通过原型链判断是否具有和数组同一原型链的顶端。)
    • arr.__proto__ === Array.prototype

数组去重如何实现?

  • 利用Set()+Array.from()
  • 利用两层循环+数组的splice方法
  • 利用数组的indexOf方法\includes方法
  • 利用数组的filter()+indexOf()

JS数组去重的方式详细总结(7种)

JavaScript中的类型转换机制

  • 显示转换
    • Number、parselnt、String、Boolean
  • 隐式转换
    • 比较运算符
    • 算术运算符

深拷贝与浅拷贝

  • 基本类型数据保存在在栈内存中
  • 引用类型数据保存在堆内存中,引用数据类型的变量是一个指向堆内存中实际对象的引用,存在栈中
  • 实现浅拷贝
    javascript
      Object.assign()
      slice
      concat
      Object.assign()
      slice
      concat
  • 实现深拷贝
    javascript
      JSON.stringify() //弊端:会忽略undefined、symbol和函数
      _.cloneDeep()
      JSON.stringify() //弊端:会忽略undefined、symbol和函数
      _.cloneDeep()

    JavaScript深拷贝和浅拷贝看这篇就够了

说一说javaScript中的原型和原型链

  • 原型:每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。
  • 原型链:任何一个对象,都有原型对象,原型对象本身又是一个对象,所以原型对象也有自己的原型对象,这样一环扣一环就形成了一个链式结构,我们把这个链式结构称为:原型链。

prototype和__proto__区别是什么?

  • prototype是构造函数的属性。
  • __proto__ 是每个实例都有的属性,可以访问 [[prototype]] 属性。
  • 实例的__proto__ 与其构造函数的prototype指向的是同一个对象。

说一说JavaScript中的作用域和作用域链

  • 作用域:作用域就是一个独立的地盘,让变量不会外泄、暴露出去。
    • 全局作用域、函数作用域、块级作用域
  • 作用域链:当在Javascript中使用一个变量的时候,首先Javascript引擎会尝试在当前作用域下去寻找该变量,如果没找到,再到它的上层作用域寻找,以此类推直到找到该变量或是已经到了全局作用域。

说一说JavaScript中的闭包

  • 闭包就是能够读取其他函数内部变量的函数。由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
  • 闭包会包含其他函数的作用域,会比其他函数占据更多的内存空间,不会在调用结束之后被垃圾回收机制回收,过度使用闭包会过度占用内存,造成内存泄漏。

typeof与instanceof

  • typeof会返回一个变量的基本类型,instanceof返回的是一个布尔值
  • instanceof可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型
  • typeof也存在弊端,它虽然可以判断基础数据类型(null 除外),但是引用数据类型中,除了function 类型以外,其他的也无法判断
  • 如果需要通用检测数据类型,可以采用Object.prototype.toString.cell(),调用该方法,统一返回格式“[object xxx]”的字符串

事件循环

概念:事件循环是JavaScript执行上下文中的一种机制,用于处理异步操作。它的核心思想是将所有的异步任务放入一个队列中,然后按照队列中的顺序依次执行,直到队列为空为止。主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为EventLoop(事件循环)。

  1. 宏任务,微任务:按照代码的书写顺序逐行执行,不需要耗时的代码,(耗时是相对的),我们称为同步代码. 需要耗时的代码我们称为异步代码,异步代码分为,宏任务与微任务
  • 宏任务:script(整体代码),setTimeout,setInterval,setImmediate(Node.js 环境),I/O,UI rendering
  • 微任务:Promise.then,process.nextTick(Node.js 环境)
  1. 事件循环的顺序:(微任务在宏任务之前的执行)
  • 首先执行所有的初始化同步代码;
  • 执行过程中同步代码执行完毕后,执行所有的微任务;
  • 执行宏任务中的一个任务,执行过程中同步代码执行完毕后,执行所有的微任务;
  • 重复执行步骤3和步骤4,直到宏任务和微任务队列都为空。

事件循环javaScript没有微任务与宏任务

ECMAScript6

var,let和const的区别?

  1. 块级作用域: 块作用域由 { }包括,letconst 具有块级作用域,var 不存在块级作用域。块级作用域解决了 ES5 中的两个问题:
    • 内层变量可能覆盖外层变量
    • 用来计数的循环变量泄露为全局变量
  2. 变量提升:var 存在变量提升,letconst 不存在变量提升,即在变量只能在声明之后使用,否在会报错。
  3. 给全局添加属性:浏览器的全局对象是 windowNode 的全局对象是 globalvar 声明的变量为全局变量,并且会将该变量添加为全局对象的属性,但是 letconst 不会。
  4. 重复声明:var 声明变量时,可以重复声明变量,后声明的同名变量会覆盖之前声明的遍历。constlet 不允许重复声明变量。
  5. 暂时性死区:在使用 letconst 命令声明变量之前,该变量都是不可用的。这在语法上,称为暂时性死区。使用 var 声明的变量不存在暂时性死区
  6. 初始值设置:在变量声明时,varlet 可以不用设置初始值。而 const 声明变量必须设置初始值。

javaScript中处理异步的几种方法?

  • 回调函数
  • Promise
  • Async/Await
  • Generator

说一说Promise

概念:Promise是异步编程的一种解决方案更加合理和更加强大

  • 状态
    • pending(进行中) fulfilled(已成功)rejected (已失败)
  • 实例方法
    • then() catch() finally()
  • 构造函数的方法
    1. all() 用于将多个 Promise实例,包装成一个新的 Promise实例
      • 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled
      • 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected
    2. race() 同样是将多个 Promise 实例,包装成一个新的 Promise 实例
      • 只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变
    3. allSettled() 接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例
      • 只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束
    4. reject() Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected
    5. resolve() 将现有对象转为 Promise对象

说一说箭头函数

  • 相比普通函数,箭头函数有更加简洁的语法。
  • 箭头函数不绑定this,会捕获其所在上下文的this,作为自己的this。
  • 使用call,apply,bind并不会改变箭头函数中的this指向。
  • 箭头函数是匿名函数,不能作为构造函数,不可以使用new命令,否则抛出错误。
  • 箭头函数没有原型对象prototype这个属性

这一次,彻底搞懂箭头函数

VUE系列

v-model双向绑定原理

  • 通过 v-bind:value 绑定变量,每次输入内容的时候触发input事件
  • 通过事件对象参数 event.target.value 获得输入的内容,并且把这个内容赋值变量

vue响应式原理

  • Vue2.0实现MVVM(双向数据绑定)的原理是通过 Object.defineProperty 来劫持各个属性的setter、getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
  • Vue 3.0实现响应式基于ES6的Proxy 区别:
    1. VUE2
    • 基于Object.defineProperty,不具备监听数组的能力,需要重新定义数组的原型来达到响应式。
    • Object.defineProperty 无法检测到对象属性的添加和删除 。
    • 由于Vue会在初始化实例时对属性执行getter/setter转化,所有属性必须在data对象上存在才能让Vue将它转换为响应式。
    • 深度监听需要一次性递归,对性能影响比较大。
    1. VUE3
    • 基于Proxy和Reflect,可以原生监听数组,可以监听对象属性的添加和删除。
    • 不需要一次性遍历data的属性,可以显著提高性能。
    • 因为Proxy是ES6新增的属性,有些浏览器还不支持,只能兼容到IE11 。
    1. 生命周期的区别
    • Vue2.0 生命周期钩子函数执行顺序
      • beforeCreate -> created -> beforeMount -> mounted -> beforeUpdate -> updated -> beforeDestroy -> destroyed
    • Vue3.0 生命周期钩子函数执行顺序
      • setup -> onBeforeMount -> onMounted -> onBeforeUpdate -> onUpdated -> onBeforeUnmount -> onUnmounted

Vue Router

  1. 路由有几种模式?说说它们的区别?
    1. hash模式:在浏览器中符号#,#以及#后面的字符称之为hash,用window.location.hash读取;
      • 特点:hash虽然在URL中,但不会被包含在HTTP请求中,对后端完全没有影响,因此改变hash不会重新加载页面。
    2. history模式:history采用HTML5的新特性;提供六种修改Url 不刷新页面的方法
      • pushState(),使用新路径
      • replaceState()替换原来的路径
      • popState() 路径的回退
      • go() 向前或者向后改变路径
      • forward() 向前改变路径
      • back() 向后改变路径
  2. router-link的属性有哪些?
    • to: 跳转的路径
    • replace: 是否替换当前路径
    • tag: 渲染的标签
    • active-class: 激活的class
    • exact: 是否精确匹配
  3. 路由懒加载是什么意思?
    • 路由懒加载的主要作用就是将路由对应的组件打包成一个个的js代码块,只有在这个路由被访问到的时候,才加载对应的组件。
    javascript
    { 
      name: "about",
      path: "/about", 
      component: () => import("../Views/About.vue") 
    }
    { 
      name: "about",
      path: "/about", 
      component: () => import("../Views/About.vue") 
    }
  4. 路由的属性有哪些?
    • name: 路由的名称
    • path: 路由的路径
    • component: 路由的组件
    • children: 子路由
    • redirect: 重定向
    • alias: 别名
  5. NotFound
    javascript
     {
        path: '/:pathMatch(.*)',
       // /:pathMatch(.*)* 解析路径
       name: 'NotFound',
       component: NotFound
     }
     {
        path: '/:pathMatch(.*)',
       // /:pathMatch(.*)* 解析路径
       name: 'NotFound',
       component: NotFound
     }
  6. 动态管理路由的方法?
    • router.addRoutes(routes)
    • router.removeRoutes(routes)
    • router.hasRoute(name) 检查路由是否存在
    • router.addRoute(parentOrName, route) 添加路由
    • router.removeRoute(name) 删除路由
  7. 路由守卫有哪些?
    1. 全局守卫
      • router.beforeEach(to, from, next) (next在Vue3中不推荐使用)
      • router.afterEach(to, from)
    2. 路由独享的守卫
      • beforeEnter
    3. 组件内的守卫
      • beforeRouteEnter
      • beforeRouteUpdate
      • beforeRouteLeave

http系列

从输入URL到页面加载的全过程?

  • URL解析
  • DNS查询
  • TCP连接
  • http请求
  • 响应请求
  • 页面渲染

URL到页面加载的全过程

前端性能优化

  • 页面加载及渲染过程的优化
    • 避免多层css
    • 避免频繁操作dom
    • 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。
    • 合理使用v-if,v-for,避免频繁操作 DOM
    • 图片加载优化(使用webp格式图片、设置适当的分辨率、使用雪碧图、使用占位图片)
    • 事件委托,利用JS事件冒泡机制把原本需要绑定在子元素的响应事件(click、keydown……)委托给父元素,减少内存占用,减少事件注册。
  • 渲染完成后的页面交互优化
    • 防抖节流

中高级前端工程师必备14种性能优化方案
前端必会的图片懒加载(三种方式)
事件委托
防抖节流
图片加载优化

移动端相关

H5如何唤醒App

  1. 需要判断设备系统,安卓和iOS分别处理
    • 安卓:Universal Links通用链接。
    • iOS:通过URL Scheme/Universal Links通用链接。
  2. 需要判断浏览器类型,微信内置浏览器和浏览器分别处理
  3. 需要判断App是否已经安装,如果已经安装,则直接打开App,否则引导用户下载App

H5唤醒App方案