前端面经【八股文合集】
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()
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中的原型和原型链
- 原型:每一个构造函数都有一个
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
(事件循环)。
- 宏任务,微任务:按照代码的书写顺序逐行执行,不需要耗时的代码,(耗时是相对的),我们称为同步代码. 需要耗时的代码我们称为异步代码,异步代码分为,宏任务与微任务
- 宏任务:script(整体代码),setTimeout,setInterval,setImmediate(Node.js 环境),I/O,UI rendering
- 微任务:Promise.then,process.nextTick(Node.js 环境)
- 事件循环的顺序:(微任务在宏任务之前的执行)
- 首先执行所有的初始化同步代码;
- 执行过程中同步代码执行完毕后,执行所有的微任务;
- 执行宏任务中的一个任务,执行过程中同步代码执行完毕后,执行所有的微任务;
- 重复执行步骤3和步骤4,直到宏任务和微任务队列都为空。
ECMAScript6
var,let和const的区别?
- 块级作用域: 块作用域由
{ }
包括,let
和const
具有块级作用域,var
不存在块级作用域。块级作用域解决了ES5
中的两个问题:- 内层变量可能覆盖外层变量
- 用来计数的循环变量泄露为全局变量
- 变量提升:
var
存在变量提升,let
和const
不存在变量提升,即在变量只能在声明之后使用,否在会报错。 - 给全局添加属性:浏览器的全局对象是
window
,Node
的全局对象是global
。var
声明的变量为全局变量,并且会将该变量添加为全局对象的属性,但是let
和const
不会。 - 重复声明:
var
声明变量时,可以重复声明变量,后声明的同名变量会覆盖之前声明的遍历。const
和let
不允许重复声明变量。 - 暂时性死区:在使用
let
、const
命令声明变量之前,该变量都是不可用的。这在语法上,称为暂时性死区。使用var
声明的变量不存在暂时性死区 - 初始值设置:在变量声明时,
var
和let
可以不用设置初始值。而const
声明变量必须设置初始值。
javaScript中处理异步的几种方法?
- 回调函数
- Promise
- Async/Await
- Generator
说一说Promise
概念:Promise是异步编程的一种解决方案更加合理和更加强大
- 状态
pending
(进行中)fulfilled
(已成功)rejected
(已失败)
- 实例方法
then()
catch()
finally()
- 构造函数的方法
- all() 用于将多个 Promise实例,包装成一个新的 Promise实例
- 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled
- 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected
- race() 同样是将多个 Promise 实例,包装成一个新的 Promise 实例
- 只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变
- allSettled() 接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例
- 只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束
- reject() Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected
- resolve() 将现有对象转为 Promise对象
- all() 用于将多个 Promise实例,包装成一个新的 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 区别:
- VUE2
- 基于Object.defineProperty,不具备监听数组的能力,需要重新定义数组的原型来达到响应式。
- Object.defineProperty 无法检测到对象属性的添加和删除 。
- 由于Vue会在初始化实例时对属性执行getter/setter转化,所有属性必须在data对象上存在才能让Vue将它转换为响应式。
- 深度监听需要一次性递归,对性能影响比较大。
- VUE3
- 基于Proxy和Reflect,可以原生监听数组,可以监听对象属性的添加和删除。
- 不需要一次性遍历data的属性,可以显著提高性能。
- 因为Proxy是ES6新增的属性,有些浏览器还不支持,只能兼容到IE11 。
- 生命周期的区别
- Vue2.0 生命周期钩子函数执行顺序
- beforeCreate -> created -> beforeMount -> mounted -> beforeUpdate -> updated -> beforeDestroy -> destroyed
- Vue3.0 生命周期钩子函数执行顺序
- setup -> onBeforeMount -> onMounted -> onBeforeUpdate -> onUpdated -> onBeforeUnmount -> onUnmounted
Vue Router
- 路由有几种模式?说说它们的区别?
- hash模式:在浏览器中符号#,#以及#后面的字符称之为hash,用window.location.hash读取;
- 特点:hash虽然在URL中,但不会被包含在HTTP请求中,对后端完全没有影响,因此改变hash不会重新加载页面。
- history模式:history采用HTML5的新特性;提供六种修改Url 不刷新页面的方法
- pushState(),使用新路径
- replaceState()替换原来的路径
- popState() 路径的回退
- go() 向前或者向后改变路径
- forward() 向前改变路径
- back() 向后改变路径
- hash模式:在浏览器中符号#,#以及#后面的字符称之为hash,用window.location.hash读取;
- router-link的属性有哪些?
- to: 跳转的路径
- replace: 是否替换当前路径
- tag: 渲染的标签
- active-class: 激活的class
- exact: 是否精确匹配
- 路由懒加载是什么意思?
- 路由懒加载的主要作用就是将路由对应的组件打包成一个个的js代码块,只有在这个路由被访问到的时候,才加载对应的组件。
javascript{ name: "about", path: "/about", component: () => import("../Views/About.vue") }
{ name: "about", path: "/about", component: () => import("../Views/About.vue") }
- 路由的属性有哪些?
- name: 路由的名称
- path: 路由的路径
- component: 路由的组件
- children: 子路由
- redirect: 重定向
- alias: 别名
- NotFoundjavascript
{ path: '/:pathMatch(.*)', // /:pathMatch(.*)* 解析路径 name: 'NotFound', component: NotFound }
{ path: '/:pathMatch(.*)', // /:pathMatch(.*)* 解析路径 name: 'NotFound', component: NotFound }
- 动态管理路由的方法?
- router.addRoutes(routes)
- router.removeRoutes(routes)
- router.hasRoute(name) 检查路由是否存在
- router.addRoute(parentOrName, route) 添加路由
- router.removeRoute(name) 删除路由
- 路由守卫有哪些?
- 全局守卫
- router.beforeEach(to, from, next) (next在Vue3中不推荐使用)
- router.afterEach(to, from)
- 路由独享的守卫
- beforeEnter
- 组件内的守卫
- beforeRouteEnter
- beforeRouteUpdate
- beforeRouteLeave
- 全局守卫
http系列
从输入URL到页面加载的全过程?
- URL解析
- DNS查询
- TCP连接
- http请求
- 响应请求
- 页面渲染
前端性能优化
- 页面加载及渲染过程的优化
- 避免多层css
- 避免频繁操作dom
- 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。
- 合理使用v-if,v-for,避免频繁操作 DOM
- 图片加载优化(使用webp格式图片、设置适当的分辨率、使用雪碧图、使用占位图片)
- 事件委托,利用JS事件冒泡机制把原本需要绑定在子元素的响应事件(click、keydown……)委托给父元素,减少内存占用,减少事件注册。
- 渲染完成后的页面交互优化
- 防抖节流
移动端相关
H5如何唤醒App
- 需要判断设备系统,安卓和iOS分别处理
- 安卓:Universal Links通用链接。
- iOS:通过URL Scheme/Universal Links通用链接。
- 需要判断浏览器类型,微信内置浏览器和浏览器分别处理
- 需要判断App是否已经安装,如果已经安装,则直接打开App,否则引导用户下载App