2024个人前端面试总结(八股文+项目情况)持续更新

JavaScript 

Js的数据类型有哪些他们的区别是什么

基本数据类型:Number String Boolean Null Undefined Symbol(ES6)

        (他们直接存储在内存中的,占用固定的内存空间。基本数据类型的赋值是通过将值直接复制给变量来完成的。)

引用数据类型 :Object Array Date Function()

        (它们存储的是对象在内存中的地址,而不是对象本身。引用数据类型的变量实际上存储的是对象在内存中的引用,通过引用可以访问和操作对象的属性和方法。引用数据类型的赋值是将对象的引用赋给变量。)

区别:
  1. 存储方式:基本数据类型直接存储数据值,而引用数据类型存储的是对象的引用
  2. 内存占用:基本数据类型占用固定的内存空间,而引用数据类型的大小取决于对象的大小。
  3. 赋值方式:基本数据类型的赋值是通过将值直接复制给变量,而引用数据类型的赋值是将对象的引用赋给变量。
  4. 传递方式:基本数据类型作为参数传递时,传递的是值的副本,而引用数据类型作为参数传递时,传递的是引用的副本。

判断数据类型有几种方法

        ypeof (缺点:typeof null的值为Object,无法分辨是null还是Object)         Object.prototype.toString.call(缺点:不能细分为谁谁的实例)

作用域和作用域链

        作用域:规定变量和函数的可使用范围称作作用域
        作用域链:每个函数都有一个作用域链,查找变量或者函数时,需要从局部作用域到全局作用域依次查找,这些作用域的集合称作作用域链

原型和原型链

        原型:原型分为隐式原型和显式原型,每个对象都有一个隐式原型,它指向自己的构造函数的显式原型
        原型链:当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去prototype里找这个属性,这个prototype又会有自己的prototype,于是就这样一直找下去,也就是我们平时所说的原型链的概念

什么是闭包

        闭包是什么:JS中内层函数可以访问外层函数的变量,使内部私有变量不受外界干扰,起到保护保存的作用,我们把这个特性称作闭包。
好处:
  • 隔离作用域,保护私有变量;有了闭包才有局部变量,要不然都是全局变量了。
  • 让我们可以使用回调,操作其他函数内部;
  • 变量长期驻扎在内存中,不会被内存回收机制回收,即延长变量的生命周期;
        坏处:内层函数引用外层函数变量,内层函数占用内存。如果不释放内存,过多时,易引起内存泄露。
        引用场景: for循环中的保留i的操作 / 防抖和节流 

内存泄露、垃圾回收机制

内存泄露:

        是指不再用的内存没有被及时释放出来,导致该段内存无法被使用就是内存泄漏,内存泄漏指我们无法在通过js访问某个对象,而垃圾回收机制却认为该对象还在被引用,因此垃圾回收机制不会释放该对象,导致该块内存永远无法释放,积少成多,系统会越来越卡以至于崩溃

垃圾回收机制:

        就是垃圾收集器按照固定的时间间隔,周期性地寻找那些不再使用的变量,然后将其清楚或释放内存。(标记清除/引用计数)

Js获取dom

  1. getElementById:通过元素的ID属性获取DOM元素。

  2. getElementsByClassName:通过元素的class属性获取DOM元素

  3. getElementsByTagName:通过元素的标签名获取DOM元素

  4. querySelector:通过CSS选择器获取DOM元素

  5. querySelectorAll:通过CSS选择器获取DOM元素

浅拷贝与深拷贝

        浅拷贝:将原对象或原数组的引用直接赋给新对象,新数组,新对象只是对原对象的一个引用,而不复制对象本身,新旧对象还是共享同一块内存。(拓展运算符。。。)
        深拷贝:开辟一个新的栈,两个对象的属性完全相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性。(JSON.stringify和JSON.parse / 递归/Object.assgin )

        优点:方便,将字符串parse后创建新对象(新地址)

        缺点:如果被拷贝的对象中某个属性的值为undefined,则拷贝之后该属性会丢失,如果被拷贝的对象中有正则表达式,则拷贝之后的对象正则表达式会变成Object

route和router的区别详解

        router是用来操作路由

        route是用来获取路由信息的。 $route.path $route.params route.query等

如何改变this指向(call、apply与bind区别)

        call、bind、apply 都是 JavaScript 中用于改变函数执行上下文(即 this 指向)的方法。

        传参 call、bind可以传递无数个参数,apply只有两个参数,第二个参数为数组

        返回 call和apply方法是直接调用函数并改变函数上下文,而bind方法则是返回一个新函数,稍后调用时绑定指定的上下文。

箭头函数和普通函数的区别

  • 箭头函数是普通函数的简写,但是它不具备很多普通函数的特性
  • this指向问题,箭头函数的this指向它定义时所在的对象,而不是调用时所在的对象
  • 没有arguments对象,不能使用arguments
  • 不会进行函数提升
  • 不能new

浏览器存储,他们的区别?

  • localStorage:永久保存,以键值对保存,存储空间5M
  • sessionStorage:关闭页签/浏览器时清空
  • cookie:随着请求发送,通过设置过期时间删除
  • session:保存在服务端

(localStorage/sessionStorage是window的属性,cookie是document的方法)

继承方式有哪些

  • 原型继承:不能传参
  • 组合继承: 调用了两次父类的构造函数,不共享父类引用属性
  • 寄生组合继承:
  • ES6的extend:子类只要继承父类,可以不写 constructor ,一旦写了,则在 constructor 中的第一句话必须是 super

常用的数组方法有哪些?

  • 改变原数组:push、pop、shift、unshift、sort、splice、reverse
  • 不改变原属组:concat、join、map、forEach、filter、slice

slice切片的意思,根据传入的起始和终止下标,获取该范围数组。splice可根据传入参数个数不同实现删除、插入操作,直接操作原数组。第1个参数为起始下标,第2个为删除个数,第3个为要增加的数据)。

Vue

Vue的生命周期

  • beforeCreate:会在实例初始化完成、props 解析之后、data() 和 computed 等选项处理之前立即调用。此时不能获得DOM节点
  • created:在这个阶段vue实例已经创建,以下内容已经设置完成:响应式数据、计算属性、方法和侦听器。然而,此时挂载阶段还未开始,因此 $el 属性仍不可用。仍然不能获取DOM元素。
  • beforeMount:在组件内容被渲染到页面之前自动执行的函数,组件已经完成了其响应式状态的设置,但还没有创建 DOM 节点。
  • mounted:在组件被渲染之后自动执行的函数。一般我们的异步请求都写在这里。在这个阶段,数据和DOM都已被渲染出来。
  • beforeUpdate:数据变化的时候自动执行的函数,此时变化后的数据还未渲染到页面之上。.
  • updated:数据变化之后自动执行的函数,此时变化后的数据已经渲染到页面之上
  • beforeDestroy:当 Vue 应用被销毁时,自动执行的函数。
  • destroyed当 Vue 应用被销毁后,且 dom 完全销毁之后,自动执行的函数。

vue子组件的生命周期?

  • 父:beforeCreate    首先初始化父原素
  • 父:created              父原素挂载数据
  • 父:beforeMounte    父原素开始挂载dom,tpl里遇到子组件
  • 子:beforeCeate      子组件开始挂载数据
  • 子:created              子元素数据挂载成功
  • 子:beforeMount      子元素开始挂载dom
  • 子:mounted            子元素dom挂载结束
  • 父:mounted            父原素dom挂载结束
  • 父:beforeUpdate     下面开始类似于dom事件流
  • 子:beforeUpdate
  • 子:updated
  • 父:updated
  • 父:beforeDestory
  • 子:beforeDestory
  • 子:destroyed
  • 父:destoryed
     

Vue双向绑定:

        vue.js是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调,

  v-model是一个指令,双向绑定实际上是Vue 的编译器完成的,通过输出包含v-model模版的组件渲染函数,实际上还是value属性的绑定及input事件监听,事件回调函数中会做相应变量的更新操作。v-bind:value = "message" v-on:input = "message=event.target.value" 作用在组件上,本质是一个父子组件通信的语法糖,通过 prop 和 .emit 实现, 等同于:value = "message" @input = "$emit('input', $event.target.value)"

keep-alive

         在组件切换过程中将状态保留在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性。

        生命周期(activated组件激活时调用/deactivated组件停用时调用)

nextTick

        在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM

Vue的Key的作用

        key主要用在虚拟Dom算法中,每个虚拟节点VNode有一个唯一标识Key,通过对比新旧节点的key来判断节点是否改变,用key就可以大大提高渲染效率

diff算法

        diff算法是指对新旧虚拟节点进行对比,并返回一个patch对象,用来存储两个节点不同的地方,最后利用patch记录的消息局部更新DOM

虚拟DOM的优缺点

        缺点:首次渲染大量DOM时,由于多了一层虚拟DOM的计算,会比innerHTML插入慢,优点:减少了dom操作,减少了回流与重绘

回流和重绘

        render树中一部分或全部元素需要改变尺寸、布局、或着需要隐藏而需要重新构建,这个过程叫做回流,回流必将引起重绘

Vue组件之间的通信方式

  • 父传子:子组件设置props + 父组件设置v-bind
  • 子传父:组件的$emit + 父组件设置 v-on
  • erf/refs:在子组件上,它的用用就指向了子组件的实例,可以通过实例来访问组件的数据和方法
  • eventBus事件总线:(不会用)

Vue路由传参的方式

        params 传参:刷新后会丢失数据
//编程式
this.$router.push({
    name: 'home', //注意使用 params 时一定不能使用 path
    params: { username: this.username },
  })

//声明式

query 传参:
//编程式
this.$router.push({
    path: '/home',
    query: { username: this.username },
  })

//声明式

computed和watch的区别

        computed是计算属性,依赖其他属性,当其他属性改变的时候下一次获取computed值时也会改变
  • 它支持缓存,只有依赖的数据发生了变化,才会重新计算。例如模板中多次用到数据拼接可以用计算属性,只执行一次计算,除非数据发生变化。
  • 不支持异步,如果有异步操作,无法监听数据的变化。
  • 如果属性值是函数,默认使用get方法,函数的返回值就是属性的属性值。还有一个set方法,当数据变化时就会调用set方法。
  • computed的值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data声明过,或者父组件传递过来的props中的数据进行计算的。
     
        watch侦听器:
  • 它不支持缓存,数据变化时,它就会触发相应的操作。
  • 支持异步监听。
  • 接受两个参数,第一个是最新的值,第二个是变化之前的值。
  • 监听data或者props传来的数据,发生变化时会触发相应操作。有两个参(immediate/deep)

map和forEach的区别

        都是循环遍历数组中的每一项 forEach和map方法里每次执行匿名函数都支持3个参数,map方法返回一个新的数组,数组中的元素为原始数组调用函数处理后的值

v-if和v-show区别?

        v-if控制Dom是否存在,v-show控制样式

为什么避免 v-if 和 v-for 用在一起

        当 Vue 处理指令时,v-for 比 v-if 具有更高的优先级,这意味着 v-if 将分别重复运行于每个 v-for 循环中,带来性能方面的浪费。

单页面应用是什么?优缺点?如何弥补缺点

  • 单页面对一个入口DOM通过路由去更改内容,整个应用只有一个html页面
  • SPA优点:用户体验好,没有页面切换就没有白屏情况;
  • SPA缺点:首屏加载慢,不利于SEO
  • SPA弥补:通过压缩、路由懒加载缓解首屏慢;通过SSR 服务器端渲染解决SEO问题;

Vue首屏优化怎么做?

  • 使用较轻量的组件,比如echart对应有vue-chart

  • vue-cli开启打包压缩 和后台配合 gzip访问;

  • 路由懒加载,分包;

  • 打包时配置删掉log日志

  • 资源过大可以使用cdn模式引入,不再打包到本地

vue2的缺陷是什么?如何解决vue2.0数组中某一项改变,页面不改变的情况?


        缺陷:数据如果为对象直接新增属性,如果为数组通过下标操作数组项,页面无法触发更新。

        对策:关于对象可以通过Vue.$set(obj,key,value),组件中通过this.$set(obj,key,value)实现新增,修改属性vue可以相应更新视图。关于数组也可以通过Vue.$set(obj,key,value),或者作者重写的那些方法来操作;

Vue.use()是干什么的

  1. 首先 Vue.use 本身是一个函数;
  2. 判断插件是否注册;
  3. 如果插件自带insatll属性,执行它,同时传递一个Vue构造函数作为第一个参数,以及use中的其他参数;
  4. 如果插件本身就是函数,执行它,同时传递一个Vue构造函数作为第一个参数,以及use中的其他参数
  5. 记录这个插件已经注册;

Vuex

        vuex是一个状态管理工具,集中式的管理所有组件的状态数据。统一的去管理组件,将组件的状态抽象为一个store文件,通过commit方法触发mutation里的函数来改变组件属性。

        五个属性state(存储) getters(获取) mutations(同步操作 /this.$store.commit(“方法名”,数据)/mapMutations) actions(异步操作 /this.$store.dispatch(“方法名”,数据)/mapActions) modules(放多个vuex)

vue组件中data为什么必须是一个函数

        防止组件重用的时候导致数据相互影响;因为data会挂在组件的原型上;所有实例共有
如果不是函数,则两个实例都引用同一个对象,当其中一个实例属性改变时,另一个实例属性也随之改变

SE6

什么是Promise

        Promise异步编程的一种解决方案。Promise是一个构造函数,接收一个函数作为参数,返回一个 Promise 实例。
let p = new Promise(function(resolve, reject){
		//做一些异步操作
		setTimeout(function(){
			console.log('执行完成Promise');
			resolve('要返回的数据可以任何数据例如接口返回数据');
		}, 2000);
	});
  • Promise对象有三种状态,他们分别是 pending(等待中) resolved(已完成)rejected(拒绝)
  • Promise.all哪怕一个请求失败了也能得到其余正确的请求结果的解决方案
  • promise 的then会返回一个新的 promise 对象,能保证 then 方 可以进行链式调用

async、await

  • Async 和 await 是一种同步的写法,但还是异步的操作,两个必须配合一起使用
  • 函数前面的async关键字,表明该函数内部有异步操作。调用该函数时,会立即返回一个Promise对象,await 是个运算符,用于组成表达式,await 表达式的运算结果取决于它等的东西,如果是promise则会等待promaise 返回结果,接普通函数直接进行链式调用
  • 如果await后面不是Promise对象, 就直接返回对应的值,只能在async函数中出现, 普通函数直接使用会报错,await语句后的Promise对象变成reject状态时,那么整个async函数会中断,后面的程序不会继续执行

宏任务和微任务有哪些?执行顺序

        宏任务:script,setTimeout,setInterval。
        微任务:Promise,process.nextTick。

        微任务会优先于宏任务执行。这意味着在当前任务执行结束后,所有微任务都会被立即执行,而宏任务只有在所有微任务执行完毕后才会执行。

箭头函数

  • 箭头函数实现了一种更加简洁的书写方式。箭头函数内部没有arguments,也没有prototype属性,所以不能用new关键字调用箭头函数。
  • 箭头函数和普通函数最大的区别在于其内部this永远指向其父级对象的this。(重点)

var  let  const的区别

  • var声明的变量存在变量提升,即变量可以在声明之前调用,var允许重复声明变量var不存在块级作用域
  • letconst不存在变量提升,即它们所声明的变量一定要在声明后使用,否则报错
  • letconst存在块级作用域l
  • letconst在同一作用域不允许重复声明变量

ES6新特性

        模板字符串,箭头函数,拓展运输符,map和set,promise和proxy,数组方法Array.from(),map()、filter(),forEach(),some()、every()---------------记得啥说啥

网络原理

从浏览器输入url后都经历了什么

  •  先进行DNS域名解析,先查看本地hosts文件,查看有没有当前域名对应的ip地址,若有直接发起请求,没有的话会在本地域名服务器去查找,没找到的话就去根服务器查找最后查找到对应的ip地址后把对应规则保存到本地的hosts文件中。
  • 进行http请求,三次握手四次挥手建立断开连接
  • 服务器处理,可能返回304也可能返回200
  • 客户端自上而下执行代码渲染页面

TCP协议和HTTP 协议

        TCP协议在建立过程中会进行三次握手四次挥手,三次握手确保双方同步并避免无效连接,四次挥手则正常终止连接或异常终止连接。

        HTTP协议是超文本传输协议(Hyper Text Transfer Protocol),是用于从万维网服务器传输超文本到本地浏览器的传送协议。HTTP是一个基于TCP/IP通信协议来传递数据的。

HTTP与HTTPS有什么区别

        HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。

如何解决前端跨域问题

        JSONP跨域,CORS,nginx代理跨域,nodejs中间件代理跨域,WebSocket协议跨域

 WebSocket和webwork

        WebSocket作用是即使通信双向通信,webwork是多进程的

性能优化

  1. 异步组件:对于较大的组件,可以使用异步组件进行延迟加载,提高页面加载速度。

  2. 列表性能优化:对于大数据列表,使用虚拟滚动或分页加载来减少渲染的数据量。
  3. 图片优化:使用适当的图片压缩和懒加载,并提供多种分辨率的图片,以避免不必要的网络负载。
  4. 组件懒加载:将页面按需加载,只加载当前视图所需的组件,减少首次加载的体积和渲染时间。
  5. 减少重绘和回流:合理使用CSS样式,避免频繁的DOM操作,以减少页面的重绘和回流。
  6. 接口过慢:后端优化接口,分段式请求接口,对加载过慢的接口做过度动画提升用户体验。
  7. 使用浏览器缓存:在合适的情况下,使用浏览器缓存可以显著减少请求时间,提高页面加载速度。

HTML&&CSS

盒模型和怪异和模型

  • 标准盒模型,总宽度 = width + border(左右) + padding(左右)+ margin(左右);高度同理。

  • 怪异盒模型(IE盒模型),总宽度 = width + margin(左右);高度同理。

让一个元素水平/垂直居中

  1. 水平居中 text-align: center;

  2. 垂直居中 margin 实现自适应居中 通过位移 transform 实现 设置 line-height 等于 height align-items: center;

  3. 对于宽度未知的块级元素 flex 布局 dispaly:flex;justify-content: center;align-item:center 绝对定位和 transform 实现, translateX 可以移动本身元素的50%

flex:1 到底代表什么 

        是一个简写方式,相当于同时设置了 `flex-grow: 1`、`flex-shrink: 1`、`flex-basis: 0` 这三个属性。满足项目的自适应需求。

算法题

回溯算法

数组arr[1,2,3] 输出结果[1, 2, 3] [1, 3, 2] [2, 1, 3] [2, 3, 1] [3, 1, 2] [3, 2, 1]

  1. 定义一个空数组res,用于存储所有的排列结果。
  2. 定义一个空数组path,用于存储当前的排列结果。
  3. 编写回溯函数backtrack,函数参数为当前的排列结果path和可选的数字列表nums。
  4. 如果nums为空,说明已经选完了所有的数字,将path加入到res中。
  5. 遍历nums中的每个数字,将其加入到path中,然后从nums中删除该数字,递归调用backtrack函数。
  6. 递归结束后,将path中的最后一个数字删除,将该数字加入到nums中,回溯到上一层。
  7. 调用backtrack函数,开始全排列。
def permute(nums):
    res = []
    path = []
    def backtrack(path, nums):
        if not nums:
            res.append(path[:])
            return
        for i in range(len(nums)):
            path.append(nums[i])
            backtrack(path, nums[:i]+nums[i+1:])
            path.pop()
    backtrack(path, nums)
    return res

arr = [1, 2, 3]
res = permute(arr)
for r in res:
    print(r)

冒泡算法排序

    // 冒泡排序
    /* 1.比较相邻的两个元素,如果前一个比后一个大,则交换位置。

   2.第一轮的时候最后一个元素应该是最大的一个。

   3.按照步骤一的方法进行相邻两个元素的比较,这个时候由于最后一个元素已经是最大的了,所以最后一个元素不用比较。 */
    function bubbleSort(arr) {
      for (var i = 0; i < arr.length; i++) {
        for (var j = 0; j < arr.length; j++) {
          if (arr[j] > arr[j + 1]) {
            var temp = arr[j]
            arr[j] = arr[j + 1]
            arr[j + 1] = temp
          }
        }
      }
    }

    var Arr = [3, 5, 74, 64, 64, 3, 1, 8, 3, 49, 16, 161, 9, 4]
    console.log(Arr, "before");
    bubbleSort(Arr)
    console.log(Arr, "after");

快速排序

    /*
    快速排序是对冒泡排序的一种改进,第一趟排序时将数据分成两部分,一部分比另一部分的所有数据都要小。
    然后递归调用,在两边都实行快速排序。  
    */
    
    function quickSort(arr) {
      if (arr.length <= 1) {
        return arr
      }
      var middle = Math.floor(arr.length / 2)
      var middleData = arr.splice(middle, 1)[0]

      var left = []
      var right = []
      
      for (var i = 0; i < arr.length; i++) {
        if (arr[i] < middleData) {
          left.push(arr[i])
        } else {
          right.push(arr[i])
        }
      }

      return quickSort(left).concat([middleData], quickSort(right))
    }

    var Arr = [3, 5, 74, 64, 64, 3, 1, 8, 3, 49, 16, 161, 9, 4]
    console.log(Arr, "before");
    var newArr = quickSort(Arr)
    console.log(newArr, "after");

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