面试题

44 阅读23分钟

CSS部分

1.     说一下CSS的盒模型

在HTML页面中的所有元素都可以看成是一个盒子

盒子的组成:内容content、内边距padding、外边框border、外边距margin

盒模型的类型:

             标准盒模型

                                 margin+padding+border+content

                         IE盒模型

                                 margin+content(border+padding)

控制盒模型的模式:box-sizing:content-box(默认值,标准盒模型)、border-box(IE盒模型)

2.     隐藏元素的方法有哪些?

display:none

opcity:0

visiblity:hidden

position:absoult

clip-path

3.     重排和重绘有什么区别?

重排(回流):布局引擎会根据所有的样式计算出盒模型在页面上的位置和大小

重绘:计算好盒模型的位置、大小和其他一些属性之后,浏览器就会根据每个盒模型的特性进行绘制

浏览器的渲染机制

对DOM的大小、位置进行就修改后,浏览器需要重新计算元素的这些几何属性,就叫重排;

对DOM的样式就行修改,比如color和background-color,浏览器不需要重新计算几何属性的时候,直接绘制了该元素的新样式,那么这里就只触发了重绘

4.什么是BFC

       

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

JS部分

1.     JS由哪三部分组成?

(1)       ECMAScript:JS的核心内容,描述了语言的基础语法,比如var、for,数据类型(数组、字符串)

(2)       文档对象模型(DOM):DOM把整个HTML页面规划为元素构成的文档

(3)       浏览器对象模型(BOM):对浏览器窗口进行访问和操作

2.     JS有哪些内置对象?

String、Boolean、Number、Array、Object、Function、Math、Date、RegExp...

 

3.     什么是闭包

函数嵌套函数,内部函数被外部函数返回并保存下来时,就会产生闭包

特点:可以重复利用变量,并且这个变量不会污染全局的一种机制;这个变量是一直保存在内存中,不会被垃圾回收机制回收

缺点:闭包较多的时候,会消耗内存,导致页面的性能下降,在IE浏览器中才会导致内存泄露

使用场景:防抖,节流,函数嵌套函数避免全局污染的时候

 

4.     前端的内存泄露怎么理解?

JS里已经分配内存地址的对象,但是由于长时间没有四方或者没办法清除,造成长期占用内存的现象,会让内存资源大幅浪费,最终导致运行速度慢,甚至崩溃的情况。

垃圾回收机制

因素:一些为生命直接赋值的变量;一些未清空的定时器;过度的闭包;一些引用元素没没有被清除

5.     时间委托是什么?

又叫事件代理,原理就是利用了事件冒泡机制来是实现,也就是说把子元素的事件绑定到了父元素的身上

阻止事件冒泡:event.stopPropagation()

addEventListener(‘click’,函数名,true/false) 默认是false(事件冒泡),true(事件捕获)

好处:提高性能,减少事件的绑定,也就减少了内存的占用

6.     基本数据类型和引用数据类型的区别?

基本数据类型:String、Numer、Null、undefined、Boolean

    基本数据类型保存在栈内存当中,保存的就是一个具体的值

引用数据类型(复杂数据类型):Object、Function、Array

      保存在堆内存中,声明一个引用类型的变量,它保存的就是引用类型数据的地址

假如声明两个引用类型同时指向了一个地址的时候,修改其中一个那么另外一个也会改变

7.     说一下原型链

原型就是一个普通对象,它是为构造函数的实例共享属性和方法;所有的实例中引用的原型都是同一个对象

使用prototype可以把方法挂在原型上,内存保存一份

__proto__可以理解为指针,实例对象中的属性,指向了构造函数的原型(prototype)

一个实例对象在调用属性和方法的时候,会依次从实例本身、构造函数原型、原型的原型上去查找,这样查找的过程就像是一个链条一样所以被称之为原型链

 

8.     new操作符具体做了什么?

(1)       先创建一个空对象

(2)       把空对象和构造函数通过原型链进行链接

(3)       把构造函数的this绑定到新的空对象身上

(4)       根据构建函数返回的类型判断,如果是值类型,则返回对象,如果是引用类型就要返回这个引用类型

9.     JS是如何实现继承的?

(1)       原型链继承

(2)       借用构造函数继承

(3)       组合式继承

(4)       ES6的class类继承

10.  JS 的设计原理是什么?

JS引擎、运行上下文、调用栈、事件循环、回调

11.  JS中关于this指向的问题

(1)       全局对象中的this指向

指向的是window

(2)       全局作用域或者普通函数的this

指向的是window

(3)       this永远指向最后调用它的那个对象

在不是箭头函数的情况下

(4)       new关键词改变了this的指向

(5)       apply、call、bind

可以改变this的指向,不是箭头函数

(6)       箭头函数中的this

它的指向在定义的时候就已经确定了

箭头函数它是没有this,看外层是否有函数,有就是外层函数的this,没有就是window

(7)       匿名函数中的this

永远指向了window,匿名函数的执行环境具有全局性,因此this指向window

 

12.  script标签里的async和defer区别?

当没有asnycn和defer这两个属性的时候,浏览器会立刻加载并执行指定的脚本

有async:加载和渲染后面的元素的过程将和script的加载和执行并行进行(异步)

有defer:加载和渲染后面的元素的过程将和script的加载并行进行(同步),但是它的执行事件要等所有元素解析完成之后才会执行
13.ES6的新特性有哪些?

(1)       新增块级作用域(let、const)

(2)       新增了定义类的语法糖(class)

(3)       新增了一种基本数据结构(symbol)

(4)       新增了结构赋值

(5)       给数组新增了API

(6)       对象和数组新增了扩展运算符

(7)       新增了模块化(import、export)

(8)       Promis

(9)       新增了set和map数据结构

(10)   新增了generator

(11)   新增了箭头函数

14.  apply、call、bind三者有什么区别

都是改变this指向和函数的调用,call和apply的功能类似,只是传参的方法不同

call方法传的是一个参数列表

apply传递的是一个数组

bind传参后不会立刻执行,会返回一个改变了this指向的函数,这个函数还是可以传参的,

bind()()

call方法的性能要比apply好一些,所以call用的更多

15.     说一下事件循环

JS是一个单线程的脚本语言

主线程、执行栈、任务队列、宏任务、微任务

主线程先执行同步任务,然后才去执行任务队列里的任务,如果在执行宏任务之前有微任务,那么要先执行微任务

全部执行完之后等待主线程的调用,调用完之后再去任务队列中查看是否有异步任务,这样一个循环往复的过程就是事件循环

16.     Promis和async/await的区别是什么?

(1)       都是处理异步请求的方式

(2)       Promise是ES6,async/await是ES7的语法

(3)       async/await是基于promise实现的他和promise都是非阻塞性的

优缺点:

1.         promise是返回对象我们要用then,catch方法去处理和捕获异常,并且书写方式是链式,容易造成代码重叠,不好维护,async/await是通过try catch进行捕获异常

2.         async/await最大的有点就是能让代码看起来像同步一样,只要遇到了await就会立刻返回结果,然后再执行后面的操作,promise.then()的方式返回,会出现请求还没返回就执行了后面的操作

17.     页面渲染的过程是怎样的?

DNS解析

建立TCP连接

发送HTTP请求

服务器处理请求

渲染页面

浏览器会获取HTML和CSS的资源,然后把HTML解析成DOM树

再把CSS解析成CSSOM

把DOM和CSSOM合并为渲染树

布局

把渲染树的每个节点渲染到屏幕上(绘制)

断开TCP连接

18.     DOM树和渲染树有什么区别

DOM树是HTML标签一一对应的,包括head和隐藏元素

渲染树是不包括head和隐藏元素

 

 

19.     了解过JWT吗?

JSON Web Token

通过JSON形式作为再web应用中的令牌,可以在各方之间安全的把信息作为JOSN对象传输,信息传输、授权

JWT认证流程

1.         前端把账号密码发送给后端的接口

2.         后端核对账号密码成功后,把用户id等其他的信息作为JWT负载,把它的头部分别进行base64编码拼接后签名,形成一个JWT(token)

3.         前端每日请求都会把JWT放在HTTP请求头的Authorizetion字段内

4.         后端检查是否存在,如果存在就验证JWT的有效性(签名是否正确,token是否过期)

5.         验证通过后后端使用JWT中包含的用户信息进行其他操作,并返回对应结果

简洁、包含性,因为Token是JSON加密的形式保存在客户端,所以JWT是跨语言的,原则上是任何web形式都支持。

20.     HTTP协议规定的协议头和请求头有什么?

(1)       请求头信息

Accept:浏览器告诉服务器所支持的数据类型

Host:浏览器告诉服务器我想访问服务器的哪台主机

Referer:浏览器告诉服务器我是哪里来的(防盗链)

User-Agent:浏览器类型、版本信息

Date:浏览器告诉服务器我是什么时候访问的

Connecton:连接方式

Cookie

X-Request-Width:请求方式

(2)    响应头信息

Location:这个就是告诉浏览器你要去找谁

Server:告诉浏览器服务器的类型

Content-Type:告诉浏览器返回的数据类型

Refresh:控制了的定时刷新

21.     说一下浏览器的缓存策略

强缓存(本地缓存)、协商缓存(弱缓存)

强缓存:不发起请求,直接使用缓存里的内容,浏览器把JS、CSS、image等存到内存中,下次用户访问直接从内存中去,提高性能

弱缓存:需要像后台发请求,通过判断来决定是否使用协商缓存,如果请求内容没有变化,则返回403,浏览器就会用缓存里的内容

强缓存的触发:

HTTP1.0:时间戳响应头

HTTP1.1:Cache-Control响应头

弱缓存的触发:

HTTP1.0:请求头:if-modified-since  响应头:last-modified

HTTP1.1:请求头:if-none-match 响应头:Etag

22.     说一下什么是同源策略

同源策略是浏览器的核心,如果没有这个策略就会遭受到网络攻击

主要指的是协议+域名+端口号三者一致,若其中一个不一样则不是同源,会产生跨域

跨域是可以发送请求的,后端也会正常返回,只不过这个结果被浏览器拦截了!

解决跨域的方式

1.         JSONP

2.         CORS

3.         Websoket

4.         反向代理

23.var let const的区别

 

24.判断一个类型的参数类型有哪些方法

        1. typeof

        2. instanceof

        3. constructor

        4. Object.prototype.toString.call

25.常见设计模式

   一、创建型模式

  1. 单例模式( Singleton Pattern
    • 定义:确保一个类仅有一个实例,并提供全局访问点。
    • 应用场景:全局状态管理(如 Redux/Vuex)、配置对象、日志记录器等。
  2. 工厂模式( Factory Pattern
    • 定义:通过工厂函数创建对象,隐藏实例化逻辑。
    • 应用场景:动态创建不同类型的组件(如按钮、输入框)或数据请求对象。
  3. 原型模式( Prototype Pattern
    • 定义:通过复制现有对象创建新对象。
    • 应用场景:组件克隆、避免重复初始化复杂对象。

二、结构型模式

  1. 适配器模式( Adapter Pattern
    • 定义:将不兼容的接口转换为客户端期望的接口。
    • 应用场景:统一不同数据源的格式(如 API 数据转换)。
  2. 装饰器模式( Decorator Pattern
    • 定义:动态扩展对象的功能而不修改其结构。
    • 应用场景:为组件添加日志、权限校验或样式增强(如高阶组件)。
  3. 代理模式( Proxy Pattern
    • 定义:通过代理对象控制对目标对象的访问。
    • 应用场景:实现懒加载、缓存请求结果、权限控制(如接口鉴权)。
  4. 外观模式( Facade Pattern
    • 定义:为复杂子系统提供统一的高层接口。
    • 应用场景:封装复杂库的 API(如 jQuery 兼容浏览器事件)。

三、行为型模式

  1. 观察者模式( Observer Pattern
    • 定义:对象间一对多的依赖关系,状态变化时自动通知观察者。
    • 应用场景:事件监听(如 DOM 事件)、数据响应式更新(如 Vue 的响应式系统)。
  2. 发布 - 订阅模式( Publish-Subscribe Pattern
    • 定义:通过事件通道解耦发布者和订阅者。
    • 应用场景:跨组件通信(如 EventBus)、消息队列。
  3. 策略模式( Strategy Pattern
    • 定义:定义一系列算法并使其可互换。
    • 应用场景:表单验证规则切换、动态选择数据请求策略。
  4. 职责链模式( Chain of Responsibility
    • 定义:将请求沿处理链传递,直到被处理。
    • 应用场景:中间件流程(如 Express/Koa)、多级表单验证。
  5. 中介者模式( Mediator Pattern
    • 定义:通过中介对象协调多个组件间通信。
    • 应用场景:聊天室消息转发、复杂组件间解耦。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

React技术

1.     受控组件和非受控组件区别?

受控组件:受控组件是指组件内部的状态完全由React的state管理,用户输入的值立即反应到组件的state中,在受控组件中,表单元素(如、、)的值不是直接由DOM本身管理,而是通过React组件的state进行控制。例如元素的值由value属性关联到组件的state变量,每次用户输入时,都会触发onChange事件,此时组件会根据事件处理函数更新state进而更新value属性,始终保持DOM元素的值跟state同步

 

非受控组件:非受控组件则是指DOM元素的值由浏览器本身管理,不受React的state直接影响。在非受控组件中,表单元素的值是由DOM自身的defaultValue属性初始化,用户输入的改变并不会立刻同步到React的state中。获取非受控组件的值常常通过ref来访问DOM节点的value属性

2.     JSX是什么?它和JS有什么区别

JSX是JavaScript的语法扩展,它允许编写类似HTML的代码。它可以编译为常规的JavaScript函数调用,从而为创建组件标记提供了一种更好的方法。

3.     组件之间如何通信,父子、兄弟可以使用什么方法

父组件向子组件通信:父组件通过props向子组件传递需要的信息

子组件向父组件同学:通过props+回调的方式

跨级组件的通信:

(1)       useContext

(2)       Redux

(3)       ref

4.     类组件中的生命周期方法

初始化挂载  constructor              初始设置state、绑定方法

挂载后      componentDidMount      数据请求、DOM操作

更新前        shouldComponentUpdate   决定是否重新渲染

更新后      getSnapshotBeforeUpdae   在最新的渲染被提交到DOM之前获取一些

卸载             componentDidUpdate      取消定时器、订阅

5.     setState是同步的吗?

异步为主:在合成事件(如onClick)和生命周期函数中,setState是异步的,React会批量合并更新以提高性能

同步场景:在setTimeout、原生事件或异步代码中,setState会同步更新并立即触发渲染

设计原因:异步批量更新减少不必要的渲染,保持状态与UI的一致性,避免平凡渲染操作DOM导致的性能问题

6.     函数组件与类组件的区别

函数组件:

(1)       函数式语法,无this

(2)       使用函数useState等Hooks

(3)       useEffect模拟生命周期

(4)       自定义hooks

(5)       默认无实例更轻量

类组件:

(1)     ES6类需继承React.Component

(2)     通过thist.state和this.setState管理状态

(3)     拥有生命周期方法(如componentDidMount)

(4)     使用HOC或Render Props逻辑复用

(5)     使用PureComponent优化性能

7.     什么是虚拟DOM

定义:内存中的轻量级DOM副本,用于高效更新真实DOM

工作流程:    

(1)     初次渲染:生成虚拟DOM树。

(2)     状态变更:生成新虚拟DOM树,通过Diff苏纳法比较差异

(3)     批量更新:仅将差异部分更新到真实DOM

优点:

        减小直接操作DOM的开销。

        批量合并更新,避免平凡回流和重绘

        跨平台能力(如React Native)

8.     为什么不能滥用useContext

useContext是React提供的一个hooks,它允许无需通过props层层传递就能访问到Context中的数据。然而,在使用过程中需要注意适度,避免滥用的原因主要有以下几点:    

(6)       过度耦合:过多的使用useContext可能导致各个组件与全局状态高度耦合,即使这些组件原本不需要关心全局状态,也可能因为直接消费Context而导致维护性和可读性下降,同时也增加了组件重构和复用的难度

(7)       难以追踪依赖:当多个组件都通过useContext访问同一个Contex时,一旦这个全局状态发生改变,所有依赖它的组件都有可能被迫重新渲染,即便它们并不关心这次状态变化的具体内容。这可能导致不必要的性能损失。

(8)       架构复杂度:随着应用的规模扩大,如果随意创建和使用Context,会导致全局状态管理变得混乱且难以维护,合理的做法通常是集中管理和划分不同作用域的Context,而不是处处使用。

(9)       不利于逻辑解耦:过度依赖全局上下文可能导致业务逻辑难以拆分和独立管理。理想的做法是将相关性的状态和逻辑封装在自包含的组件或Redux等状态管理库中,保持每个模块的内聚性

9.     什么是高阶组件、组件本质是什么、什么是高阶函数

高阶组件是重用组件逻辑的高级方法,是一种源于React的组件模式,HOX是自定义组件,在它之内包含另一个组件。它们可以接受子组件提供的任何动态,但不会修改或复制其输入组件中的任何行为。你可以认为HOC是纯组件

 

React中的高阶组件是一种高级的React组抽象概念,它本质上是一个函数,此函数接受一个React组件作为参数,并返回一个新的封装过的React组件。高阶组件主要用于代码复用、逻辑抽象和交叉关注点的处理,比如权限控制、数据预取。主题样式切换等

10.  如何获取子组件内的元素引用

函数组件:需要forwardRef和useImperativeHandle

11.  Hooks有什么优点吗,为什么会有hoosk

优点:

          逻辑复用:自定义Hooks解耦业务逻辑

          代码简洁:消除类组件的this和生命周期复杂性

          性能优化:useMemo/useCallback减少不必要的i计算和渲染

          渐进式:无需重写现有类组件,逐步迁移

原因:解决类组件逻辑分散、难以复用、this绑定等问题,低通函数式编程范式。

12.  useState、useEffect、useContext、useReducer、useCallback、useMemo 、useRef等的用法

useState:管理组件状态

useEffect::处理副作用(数据获取、订阅等)

useContext:跨组件传递数据

useReducer:复杂状态逻辑(如Redux)

useCallback:缓存函数,避免子组件不必要的渲染

useMemo:缓存计算结果

useRef:访问DOM或者保存可变值(不触发渲染)

13.  自定义hooks

const useRequest= (rul)=>{

const [data,setData] = useState()

useEffect(()=>{

          fatch(url).then(res=> res.json()).then(setData)

},[url])

        return data

}

const data= useFatch(‘api/data’)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Webpack配置

1.   入口(entry)

配置Webpack的打包入口,可以是单个入口,如指定main.js作为文件入口;也可以是多个入口,通过entry对象的键值形式配置多个入口点

2.   出口(output)

配置Webpack打包后输出的文件路径、文件名等

3.   模式(mode)

通过mode选项配置Webpack的运行模式,包括development(开发模式,会启用很多优化开发体验的特性)和production(生产模式,会启用很多优化输出结果的特性)

4.   模块(module)

配置Webpack如何处理项目中的不同类型的模块

5.   解析(resolve)

配置Webpack如何查找模块

6.   插件(plugins)

使用Webpack插件来扩展Webpack的功功能

示例配置(使用HtmlwebpackPlugin插件自动生成HTML文件并自动引入打包后的资源)

7.   开发服务器(DevServer)

配置Webpack的开发服务器,提供实时重载等功能

8.   优化(Optimization)

配置Webpack的优化选项,如代码分割、压缩等

使用splitChunks进行代码分割

Webpack的构建流程

1.   初始化流程:

·读取参数:从webpack配置文件(webpack.config.js)和Shell语句中读取与合并参数,得出最终的参数

2.   读取webpack配置文件

·通过webpack.config.js或命令行参数传入配置,确定项目使用的插件、loader和入口文件等

3.   解析模块依赖

·从入口文件开始,webpack会找到应用程序的所有依赖模块,并分析它们的依赖关系,形成一个依赖图谱

4.   加载和转换模块

·根据模块类型(如JavaScript、css、图片等),webpack会选择相应的loader对模块进行加载和转换。例如使用babel-loader转换ES6+的JS代码,使用css-loader和style-loader处理css文件

5.   插件处理

在实际打包过程中,Webpack可能会使用插件来执行一些额外的操作,如压缩代码(使用terser-webpack-plugin)、提取公共代码(使用mini-css-extract-plugin)、代码分离等。

6.   创建Chunks和Bundle:

6.    Webpack会将模块组合成Chunks,这些Chunks可以是基于入口文件的,也可以是基于动态导入或代码分割产生的。

6.    然后,Webpack会将Chunks转换成最终的Bundle,这是源代码文件经过加载、编译、处理和优化后的产物。

7.   输出打包结果:

7.将所有被处理后的模块(Bundle)输出到指定的目录中,这个目录在Webpack配置文件的output属性中指定。

8.   启动webpack-dev-server(如果配置):

8.如果运行的是开发服务器(使用webpack-dev-server),Webpack会启动一个本地服务器,用于监控文件变化、实现热更新等开发相关功能。

如何使用 webpack 对项目进行优化

1.减少入口文件大小

      将入口文件拆分为多个较小的模块,使用动态导入(dynamic imports)按需加载,减少初始加载的文件大小。

2.代码分割(Code Splitting)

通过配置Webpack的代码分割功能,将项目代码分割成多个块(chunks),并在需要时按需加载。这可以通过Webpack的optimization.splitChunks配置实现。

3.使用Tree Shaking

通过配置Webpack的Tree Shaking功能,只保留项目中实际使用到的代码,剔除未使用的代码,减少打包后的文件大小。在Webpack 4+中,Tree Shaking是默认开启的,但可能需要配置optimization.usedExports为true来确保启用。

4.优化加载速度

使用Webpack的插件如MiniCssExtractPlugin来提取CSS代码,以减少构建时间和加载时间。

使用babel-loader的缓存机制,避免重复编译已处理的文件。

5.并行构建

使用Webpack的thread-loader或happypack插件将任务分发给多个子进程并行处理,提高构建速度。

6.优化文件体积

使用Webpack的压缩插件如terser-webpack-plugin来压缩JavaScript代码,使用cssnano等工具压缩CSS代码,减小文件体积。

7.使用缓存

配置Webpack的缓存功能,使得构建过程中只重新构建发生更改的部分,而不是每次都重新构建整个项目。这可以通过cache配置项来设置。

8.懒加载与预加载

对于大型应用,使用Webpack的懒加载(Dynamic Import)功能,按需加载非关键性资源。

同时可以使用预加载(Preload)和预解析(Prefetch)机制提前加载关键资源。

9.优化图片资源

对于图片资源,可以使用Webpack的url-loader或file-loader来压缩和处理图片,并根据需要进行懒加载或响应式加载。

结合image-webpack-loader插件,可以对图片进行更进一步的优化,如压缩、哈希命名等。

10.配置合理的模块解析规则:

通过配置Webpack的resolve选项,设置合适的模块解析规则,避免过多的文件查找和解析过程。

11.优化module.noParse配置

让Webpack忽略没有采用模块化的文件进行递归处理,提高构建性能。可以通过配置module.noParse来实现。

12.使用自动刷新和文件监听

在开发环境中,使用Webpack的自动刷新功能,如webpack-dev-server,可以实时看到代码变更的效果。

配置文件监听选项,如watchOptions,来优化文件变化时的处理性能。

13.压缩代码

除了使用TerserPlugin压缩JavaScript代码外,还可以使用其他插件或工具来压缩HTML和CSS代码。

14.提取公共代码

使用Webpack的插件(如SplitChunksPlugin)将公共代码抽离为单独的文件,减少重复加载和缓存压力。

以上这些优化策略和方法可以根据项目的具体需求和场景进行灵活应用。需要注意的是,不同的优化策略之间可能存在相互影响,因此需要综合考虑以达到最佳效果。