2024-面试题整理

238 阅读19分钟

面试题all

1.为什么写key

Key会给每一个vnode增加一个唯一的id,可以依靠key更快更准确的找到oldvnode中对应的vnode节点,避免了就地复用的情况

2.Es5的继承和Es6除了写法以外还有什么区别

子类this的生成顺序不同,Es5的继承先生成了子类实例再调用父类的构造函数修饰子类。Es6则是先生成父类,再通过 super继承父类的实例方法

3.构造函数和class类的一个区别

1、class声明会提升但不会赋值,在class定义前new会报错,类似let const的暂时性死区

2、Class内部会采用严格模式,使用未声明的变量会报错。且必须使用关键词new

3、构造函数new完后还可以new子类,会继承prototype,但class不会

4. JS异步解决方案的发展历程和优缺点

1. 回调函数。回调地狱,不能很好的处理错误。耦合性太强,解决了同步的问题

2. 为了解决callback诞生了promise。链式调用解决了回调函数,缺点无法取消promise,错误需要配合回调函数来补货

3. Generator 可以控制函数的执行

4. Async await 优点代码清晰。不需要写一堆.then,缺点 需要try catch补获错误,并且如果多个请求依赖性不强使用await会将异步变为同步,造成性能的降低

5.实现一个new

function myNew(fn,...args){
const obj = Object.create(fn.prototype)
const res = fn.apply(obj,arg)

return res intanceof Object?res:obj
}

6.判断数据类型及其优缺点

1、typeof 只能判断基本数据类型。判断引用类型时除了function其他都是object,还有null也是object
2、instanceof判断对象是不是某个构造函数的实例。不能判断基本数据类型
手动实现一个
function myInstanceof(data,type){
if(data.proto === type){
return true   while(data){
data= data.__proto

}

}
return false
}
3、使用Object.prototype.tostring.call()

7.简单讲解一下http2的多路复用

为了解决http1,的对头阻塞诞生的。在http1.0的时候每发起请求都会进行一次tcp连接(三次握手四次挥手),后面1.1的时代默认开启了keepalive,同一个链接可以发多个请求,但是也要等服务器接收后才发下一个,也就是说同一时刻只能发一个http请求,这样就会造成一个队头阻塞。在http2中将文本格式传输改为了二进制传输。帧和流,多个帧组成流,多路复用其实就是一个tcp链接可以存在多个流,通过帧中的标识可以知道是哪个流(请求)避免了队头阻塞提高了传输性能。http3的话为了解决tcp丢包(对头阻塞)的问题,基于udp协议的quic协议

8. npm模块的安装机制,为什么输入install会自动安装模块

发出指令会查询nodemodules中是否已存在,若存在不按照,若不存在,npm向registry查询模块压缩包的网址,下载压缩包,解压到nodemodules目录

9. Vuex和redux的区别

都是通过state共享数据,他的状态都是可预测的,在组件内部需要调用特定的方法进行状态变更。单例模式,流程类似。不同点在于vuex他是可变数据,通过mutaion直接对数据进行修改,而redux是通过reduce纯函数,set新值覆盖旧值。发送请求时vuex可以直接action里发送请求,redux则需要借助中间件 redux-thunk

10. 介绍下观察者模式和发布订阅模式的区别,各适用于什么场景

观察者模式中主体和观察者是相互感知的,比如vue的 watcher Dep
而发布订阅模式是借助第三方实现调度的,发布者订阅者之前互不感知

11.讲一下js常见的继承方式

1.原型链继承
将子构造函数的原型指向父构造函数的实例。子构造函数可以访问父构造函数的方法和属性。缺点,改父构造函数会影响子构造函数
function Parent(){} Child.prototype = new Parent() 2.构造函数继承(经典继承) 通过在子构造函数中调用父构造函数实现。子构造函数中通过call和apply方法,将父构造函数的上下文设置为子对象的上下文,实现继承。缺点只继承了实例属性,无法继承原型对象上的方法 function Parent(name){this.name = name} function(name){Parent.call(this,name)} 3.组合继承 结合了原型链继承和构造函数继承,既继承了父构造函数的属性又继承了方法。缺点,调了两次父构造函数,一次是创建原型一次是创建子对象。 4.寄生式继承 创建一个仅用于封装继承过程的函数,该函数在内部以某种方式增强对象

  funtion objectCopy(obj){
     function fun(){}
     fun.prototype = obj
     return new fun()
 }
 function create(obj){
   const clone = objectCopy(obj)
   clone.handler = ()=>{
       console.log(1111)
   }
   return clone
}

5.寄生组合继承 减少组合式继承带来的二次调用带来的性能开销

   function inheritPrototype(child,parent){
        // 创建一个中间对象 该对象的原型是父类的原型
        var prototype = Object.create(parent.prototype)
        // 将中间对象的构造函数指向子类
        prototype.constructor = child
        // 再将子类的原型设置为中间对象
        child.prototype = prototype
    }

image.png

12.cmd amd umd 和CommonJs Esm的区别

cmd是Require.js提出的模块化规范,amd是SeaJs提出的,都是浏览器环境的模块化,通过define定义require引入,强调异步加载,cmd不同处在于他是按需执行的, umd则是一种通用的模块规范,umd可以同时支持amd commonjs CommonJs主要用于服务端开发,通过module.exports导出,通过require函数加载。CommonJs模块是同步加载的
ESmodule是官方标准模块化,可以进行更好的静态分析和性能,export和import。

13接口安全如何做

使用awes加密,他的库叫crypto.js,服务端使用相同的加密规则

14.webpack

他是一个静态模块打包工具。可以很好的管理模块依赖,编译出模块需要的静态文件
构建过程
1.初始化阶段:从配置文件和命令中进行参数合并,并初始化插件等执行环境所需要的参数
2.编译构建阶段  从entry出发,针对每个module串行调用对应的loader去翻译文件内容,再找到该module所依赖的module递归编译处理
3.输出阶段 对编译后的module进行一个合并,合并成chunk,转换成文件bundle(可放浏览器直接运行)。
loader

loader本质是一个翻译函数。因为webpack只认识js,loader则对img css等进行一个转译处理 常见的有 style-loader css-loader less-loader postcss,url,file-loader,babel,eslint;

plugins

就是插件。可以扩展webpack的功能。他会贯穿webpack的整个编译周期。例如打包优化,资源管理,环境变量注入等。
常用插件 html-webpack-plugin 可以构建多个html和入口 bundle analyse

15.vite

vite是一个基于ES模块的前端构建工具,利用浏览器原生的es模块系统,通过将代码进行拆分,实现了快速冷启动和热模块替换。热模块替换更改配置文件也可以更新。

16.如何理解js的异步

js是一门单线程语言,他运行在浏览器的渲染主线程,而渲染主线程只有一个,他不仅要执行js还要渲染页面,使用异步的方式不进行阻塞。比如计时器,网络请求会交给其他线程去处理。将回调函数包裹成任务加入到消息队列的末尾等待浏览器调度执行。首先是执行

17.Es'6和commonjs模块的相同点和区别

commonjs是运行时加载, Es'6是编译时
commonjs是同步加载多用于服务端,es6是异步加载
commonjs是对模块的浅拷贝,es6只存只读不改变值,指针指向不变

18.说说你对ES6 module

19.使用迭代的方式实现flatten函数

解构
function flatten(arr){
    while(arr.some(item=>Array.isArray(item))){
        arr = [].concat(...arr)
    }
}
递归
function flatten(arr){
    const res = []
    for(const item of arr){
        if(item.constructor ==='Array'){
            res.concat(flatten(item))
        }else{
            res.push(item)
        }
    }
    return res
}

20 为什么Vuex的mutaion和reducer不能做异步操作

因为修改单一数据源state的函数必须是纯函数。统一输入就会统一输出。没有任何副作用。如果是异步则会引入副作用,导致更改后的state不可预测

21.HTTPS握手过程中如何校验证书的合法性

看是否是信任的有效证书,校验是否被吊销,校验证书是否在有效期内,校验私钥是否和证书颁发的一致。

Vue面试题

1.谈谈你对Vue的理解

Vue是用户构建用户界面的渐进式框架。是一个逐一递增的过程(声明式渲染->组件系统->客户端路由->大规模状态管理->构建工具(webpack vite)) 1声明式框架更加关注结果而不去关注过程,(非命令式)
2MVVM模式
3.采用虚拟dom
4.区分编译时(打包)和运行时(浏览器)
Vue专门写了个编译时可以将模板编译成虚拟dom(生成render函数),在构建的时候进行模板编译运行的时候不需要 5.组件化 提示开发效率和复用性,降低更新范围,只重新渲染变化的组件

2.谈谈你对SPA 的理解

单页面应用,对应MPA。现在我们用vue和react编写页面都只有一个html页面,并且提供一个挂载点,打包后通过js去动态渲染页面。包括页面切换也是通过前端路由。 单页面与多页面的对比,spa局部刷新,页面切换更快,体验更佳,更容易维护,但是不利于seo优化

3.Vue为什么需要虚拟dom

template会被编译器编译成渲染函数render,挂载时会调用render函数,返回的对象就是虚拟dom。然后在后续的patch过程中进一步生成真实dom 挂载结束后会记录第一次生成的VDOM,当响应式数据发生变化时组件会重新render,生成新的vdom。然后再进行diff操作对比,将更改的部分改到真实dom上从而进行最小量的dom操作更新视图

4.谈谈对Vue组件化的理解

组件化是对ui的封装,模块化是对业务的封装。提升开发效率测试性和复用性,降低了更新范围,只渲染变化的组件
vue中每一个组件都有一个渲染函数watcher,effect(vue3),vue是组件级更新,要合理的拆分组件,否则更新页面都要全页面更新

5.既然Vue可以数据劫持探测到数据变化,为什么还要虚拟dom diff检测差异?

Vue是组件级更新,每个组件都有一个渲染watcher,如果一个属性一个watcher会导致大量watcher的产生浪费内存

6.请你说下对响应式数据的理解

7.Vue中如何检测数组变化

8.Vue中如何进行依赖收集

每个属性都拥有自己的dep属性,存放他所依赖的watcher(通过dep.target保存下来),当属性变化后会通知对应的wathcer更新
Vue3中默认会通过map结果将属性和effect映射起来,初始化时会调用render函数,此时会触发属性依赖收集track,属性发生变化时找到对应的effect列表依次执行trigger

9.Vue.set是如何实现的

vue不允许在已经创建的实例上动态新增新的响应式属性。他会拿到当前的__ob__自身的响应式对象,如果是数组,内部采用了splice来实现。如果是对象修改如果修改 即可。

10.computed和watch区别

Vue2中有三种watcher,渲染watcher,计算属性watcher(computed),用户watcher(watch)
computed内部维护了一个属性dirty为true。初始化时为true,再次取值时dirty为false返回缓存的值。当属性变化时 dirty为true 会触发更新,重新计算新的值。

他的内部计算属性wathcer不会立即执行。lazy为true,内部会通过Object.defineproperty定义成一个属性。当用户取值时会调用get方法,找到对应的wathcer,看dirty决定是否触发运算。同时计算器属性watcher会触发到渲染watcher(watcher.depend()方法)。watch的底层就是new 了一个用户watcher,数据一变化就执行

watch连续输入两个字符时会产生取的上一个值,vue3采用了onCleanup

11.ref和reactive的区别

reactive主要处理对象类型的数据响应式。底层采用 new Proxy() ref通常处理单值的响应式,ref主要解决原始值的响应式问题。底层还是用了Object.definePxxx(),ref会将普通对象包装成一个refimpL对象,如果是对象的话会进行proxy代理,在reactive中使用ref会进行拆包操作。

12.watch和watchEffect区别

watchEffect支持传入一个函数会立即执行,被动的追踪他的依赖,依赖变化重新执行。数据变化后调用scheduler内部会再次触发effect.run()重新执行getter
const effect = new ReactiveEffect(getter,scheduler)
effect.run() watch侦测数据源变化时会调用回调函数。 scheduler内部调用cb

13.如何将template转换成render函数(编译时完成)

1.将template模版转换成ast语法书-parseHTML
2.遍历语法树找到静态节点,跳过diff操作对静态语法做模板标记,markup diff来做优化的静态节点跳过diff操作
3. 重新生成代码(字符串拼接),用with包裹通过new function的方式产生render函数

14. new Vue的过程发生了什么

1.内部会进行初始化操作init
2.初始化组件的事件,初始化组件的父子关系(parentparent children root)调用beforeCreated3.初始化provideinject(跨级通信)。初始化响应式数据(initState),再看一下用户是否传入render函数(优先级更高)或者el属性和template。调用created4.看用户有没有eltemplate进行root) 调用beforeCreated 3.初始化provide inject(跨级通信)。初始化响应式数据(initState),再看一下用户是否传入render函数(优先级更高)或者el属性和template。 调用created 4.看用户有没有el和template进行mount挂载 5.内部挂载的时候会产生一个watcher会调用render函数进行依赖收集,内部还会给响应式数据增加dep属性,让属性记录当前的wathcer,vue更新的时候采用虚拟dom的方式进行diff算法更新

15.Vue的声明周期

创建前后,挂载前后,更新前后,卸载前后。服务端没有mounted钩子可以采用creted进行替代。
Vue3中的区别,setup替代了 beforeCreated和created,卸载时改为了beforeUnmount,unmounted

16.Vue中的diff算法原理

内部采用深度递归(比较父后比较子,再比较孙)和双指针的方式进行比较
比较过程 
先比较是不是相同节点 key 和tag
相同节点比较属性,并复用老节点(复用真实dom,只改属性)
比较儿子节点  新增改,两个儿子都是列表时(比对差异)后进行更新
优化比较:头头,尾尾,头尾,尾头

17.Vue.use是干什么的

安装Vue插件,如果是一个对象需要抛出install方法,如果是函数直接被作为install方法, 会将Vue作为参数传入,插件中不需要依赖Vue。防止Vue的版本出现冲突。
插件的功能:
1.添加全局指令,过滤器,组件
2.添加Vue实例方法
如何实现
1. Vue.use就是一个函数,通过调用use,执行这个函数。会判断当前插件是否在存在,如果不存在直接执行install方法并且传入参数改变this

18.Vue.extend方法的作用

他是一个Vue构造器函数,参数是组件选项的对象创建一个子类。data是传入一个函数。返回的是一个构造器子类。需要mounted去挂载。内部实现其实是创建一个子类,继承了Vue父类的方法和原型,也会在内部调Vue的初始化。
1.所有的组件都市通过Vue.extend构造出来的 2.可以通过此方法手动挂载组件(new 这个构造器)。在vue3里是render函数,或者telepot。 3.后端存储的字符串模板可以通过Vue.extend进行渲染。

19.函数组件的优势

无状态无生命周期无this,性能比较高。正常创建的组件会通过Vue.extend构造出的子类再去new,并且不会初始化父子组件关系。可以在template中写functional。Vue3中不存在new了,无优势了就。

20 V-once

缓存节点或者组件,只有在初始化时渲染,后续会当成静态节点提高性能。缺点是如果数据需要变化,不会重新渲染。  Vue3中增加了V-memo,指定某些属性可以数据变化重新渲染

21 Vue.mixin的使用场景

mixin可以扩展组件,将公共逻辑抽离。在需要该逻辑的时候进行混入。内部实现是采用策略模式针对不同的属性进行合并。合并策略。缺点命名冲突和数据来源问题。Vue3使用compositionAPI提取公共逻辑。

22.hash和history的区别

hash不会向浏览器发请求。historyhash兼容性更强,可以在本地文件环境中运行

Ts相关

1.类型批注

2.类型推断

3.数据类型

boolean number string array,tuple元祖 enum枚举 any,null.void,undefined,never,object  
元祖:已知数量,已知类型  
let turpleArr :[number,string,boolean]
nullundefined是其他类的子类,赋值给stringnumber类型  
never是任何类型的子类型代表不会出现的值。最底层  
void 空类型,方法没有返回值

4.谈谈你对ts中高级类型的理解

1.交叉类型

    T&U 同属于两种类型 
    interface parent{name:stirng} 
    interface child{age:number}
    type Person = parent & child
    const name:Person = {name:'测试',age:2}

2.联合类型

   或。多个类型中的任意一个。 T|U

3.类型别名

 type some = xxx
 type some = boolean | string
 const a:some = true

4.类型索引

 keyof 类似Object.keys
 interface Button {
     type:string
     text:string
 }
 type ButtonKeys = keyof Button 输出key的联合类型 type|text. 多用于遍历

5.类型约束

 extends
 type BaseType = string | number |boolean
 function copy<T extends BaseType>(args:T):T{
     return arg
 }

类型约束,类型索引一起用

    k必须是T的索引才可以返回正确值
    function getValue<T,K extends keyof T>(obj:T,key:K){
        return obj[key]
    }

6. 类型映射

对所有类型变成只读
type Readonly<T>={
    readonly [p in keyof T]:T[P]
}
interface Obj{
    name:string
    age:string
}

type ReadOblyObj = Readonly<obj>  会将Obj变为only的属性 

7.条件类型

T extends U ? X:Y   T是U的子集,使用X否则使用Y
Ts的高阶类型可以覆盖更多的场景。

5.说说你对接口的理解,应用场景

方法特征的集合。interface 
interface IUser{
    name:string
    age:number
}
可以给调用方知道传参和出参。
const getUserInfo = (user:IUser):string=>{
    return `name:${user.name},age:${user.age}`
}

6.ts中的类

类是一种用户定义的引用类型。ts中全面支持了面向对象编程的内容。比es6多了 类 接口 class

  1.字段:定义类中的变量
  2.构造函数:类实例化时调用,为类的对象分配内存
  3.方法:对象中要执行的操作
  class Car{
      engine:string;
      constructor(enigine:string){
          this.enigine = enigine
      }
      post():void{
          console.log('')
      }
  }

7.什么是ts

ts是js的超集
支持ES6的语法
支持面向对象编程的概念。类  接口  继承 泛型
ts不支持在浏览器中运行,需要编译成js

8.为什么要用ts,他的优势有哪些

增加了静态类型。代码质量更好。
优势:类型一定程度上充当文档。不需要翻源码,编辑器自动填充,自动联想。

9.const readonly的区别

const防止变量值被修改,readonly 防止变量属性被更改
接口和类型别名都可以描述对象或函数类型。类型别名可以用于其他类型,基本类型,联合类型 ,元组

10.any never unknow null undefined void的区别

any 动态类型变量,失去了类型检查的作用
never 永远不存在的值的类型。(抛出异常,不会有返回值的箭头函数)
unknow 任何类型的值都可以赋值给 unknow。
null undefined 默认是所有类型的子类型。可以赋值给其他类型(如number的变量)。如果开启了strictNullCheck只能赋值给自身或voidvoid 没有任何类型

11 ts中用interface给Function Array Class做声明

interface Say{
    (name:string):void
}
let say:Say = (name:string):void=>{}
interface NumberArray{
    [index:number]:number
}
const list:NumberArray = [1,2,3]
interface Person{
    name:string
    sayHi(name:string):string
}

12.Ts中Union Types(联合类型)需要注意哪些

  只能访问共同的属性和方法,否则会报错

13.ts中如何使用联合类型的key

 enum str{A,B,V}
 type strUnion = keyof typeof str; 'A'|'B'|'C'

14.ts中type和interface的区别

 相同点:都可以描述对象和函数,都允许拓展
 不同点:
 type类型别名可以声明基本类型,联合类型 元组,interface只能声明对象类型 
 type 可以使用typeof获取实例类型进行赋值
 多个interface可以自动合并。,type不行。
 interface可以通过extends实现继承,type需要使用&符

15.简单聊聊你对ts类型兼容性的理解

   类型兼容,相同结构会被认为是同一类型。接口兼容,X=Y Y中的成员必须更多

16.Ts中对象展开会有什么副作用

   后面的属性会覆盖前面的属性,仅包含可枚举的属性。不可枚举的属性会丢失。

17.declare,decclare global是什么

      declare 定义全局变量,全局函数,全局命名空间 js modules classdeclare global为全局对象, window增加新的属性

18.ts类中public private,protected,readonly修饰符的理解

   private 私有 类内部访问
   protected 类内部以及类子类访问

19. keyof和typeof关键词的作用

   keyof对索引类型查询操作符,获取索引类型的属性名,来构成联合类型