知识记录

面试题

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

基本数据类型:Number String Boolean Null Undefined Symbol(ES6) (直接存储在内存中的,占用固定的内存空间;赋值是通过将值直接复制给变量来完成的;作为参数传递时,传递的是值的副本)

引用数据类型 :狭义的对象(object)、 数组(array)、函数(function) (引用数据类型的变量实际上存储的是对象在内存中的引用,通过引用访问和操作对象的属性和方法;赋值是将对象的引用赋给变量;作为参数传递时,传递的是引用的副本)

2.判断数据类型有几种方法?

typeof运算符:

无法分辨Array、null和Object

instanceof 运算符:

原理是检查右边构造函数的原型对象(prototype),是否在左边对象的原型链上;

等同于:右边构造函数.prototype.isPrototypeOf(左边对象)

因此同一个实例对象,可能会对多个构造函数都返回true。

var d = new Date();
d instanceof Date // true
d instanceof Object // true

Object.prototype.toString.call方法

不能细分为谁谁的实例

3.作用域和作用域链

作用域:规定变量和函数的可使用范围称作作用域

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

4.原型和原型链

原型:原型分为隐式原型和显式原型,每个对象都有一个隐式原型,它指向自己的构造函数的显式原型 ​ 原型链:每个对象都拥有一个原型对象,同时原型对象也可能拥有原型,这样一层一层,最终指向 null ,这种关系被称为原型链。

5.什么是闭包?

闭包是什么:JS中内层函数可以访问外层函数的变量,使内部私有变量不受外界干扰,起到保护和保存的作用,我们把这个特性称作闭包。 好处: 隔离作用域,保护私有变量; 让我们可以使用回调,操作其他函数内部; 变量长期驻扎在内存中,不会被内存回收机制回收,即延长变量的生命周期; 坏处:内层函数引用外层函数变量,内层函数占用内存,如果不释放内存,过多时,易引起内存泄露。 引用场景: for循环中的保留i的操作 、防抖(多次触发只执行最后一次)和节流 (规定时间内只触发第一次)

6.内存泄露、垃圾回收机制

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

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

7.浅拷贝和深拷贝

浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。

深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改变原对象。

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

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

传参: call、bind可以传递很多个参数,apply只有两个参数,第一个参数要绑定的对象、第二个参数为数组;

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

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

箭头函数是普通函数的简写,但是它不具备普通函数的很多特性;

箭头函数的this指向它定义时所在的对象,而不是调用时所在的对象;

没有arguments对象,不能使用arguments;

不会进行函数提升;

不能作为构造函数。

10.什么是web缓存?

web缓存主要指的是两部分:浏览器缓存和http缓存。

浏览器缓存包括:

localStorage:永久保存,以键值对保存,存储空间5M。  sessionStorage:关闭页签/浏览器时清空,存储空间5M。  cookie:随着请求发送,通过设置过期时间删除(大概4kb)。  localStorage/sessionStorage是window的属性,cookie是document的属性。

http缓存:当Web请求抵达缓存时, 如果本地有“已缓存的”副本,就可以从本地存储设备而不是原始服务器中提取这个文档。

优点:

减少不必要的网络传输,节约宽带(就是省钱)

更快的加载页面(就是加速)

减少服务器负载,避免服务器过载的情况出现。(就是减载)

缺点:占内存(有些缓存会被存到内存中)

http缓存又分为强制缓存和协商缓存。

11.从 URL 输入到页面展现到底发生什么?

首先进行 DNS 域名解析,找到真实 IP ,向服务器发起请求,服务器交给后台处理完成后返回数据,浏览器接收⽂件( HTMLJSCSS 、图象等)并对加载到的资源进⾏语法解析,从而构建DOM树等相应的内部数据结构并渲染页面。

12.setState是同步的还是异步的?

setState在React合成事件和生命周期方法中是异步的,在setTimeout和原生事件处理中是同步的。但是,从React 18开始,所有的setState调用都会自动进行批量处理(异步)。

13.前端性能优化

网络优化:压缩资源、代码拆分(比如拆分成多个小模块按需加载)、CDN加速、优化http请求(比如合并小图标为雪碧图、内联关键 CSS 和 JavaScript 代码等)

页面渲染优化:

懒加载;

防抖和节流;

避免重排与重绘:

直接操作 DOM 元素几何属性、改变布局等会触发重排;修改元素颜色、背景等外观属性引发重绘。减少这类操作,批量修改样式,使用 CSS 过渡动画替代即时 DOM 变动;

优化CSS加载顺序:将关键 CSS (用于首屏渲染的样式)放在文档头部加载,确保页面渲染样式尽早可用;非关键 CSS 放底部异步加载,不阻塞页面渲染。

内存管理优化:

及时清理变量:不再使用的变量、定时器、事件监听,要适时清除,防止内存泄漏;

合理使用闭包。

代码质量优化:

移除冗余代码;

优化算法逻辑;

Tree Shaking(它自动剔除未引入模块代码,打包出更精简的生产包)。

14.前端首屏加载优化

减少入口文件体积,例如使用路由懒加载;

静态资源本地缓存,例如采用http缓存和localStorage;

UI框架按需加载,例如Element UI按需引入要使用的组件;

组件重复打包,例如在webpackconfig文件中,修改CommonsChunkPlugin的配置minChunks: 3,表示会把使用3次及以上的包抽离出来,放进公共依赖文件,避免了重复加载组件

图片资源压缩,例如使用雪碧图、使用线字体图标

使用SSR。

15.为什么使用vuex?

组件间数据共享:Vuex可以将数据共享给多个组件共同使用,避免了数据的重复获取和频繁更新,提高了代码的效率和复用性。

复杂状态的管理:当项目中的状态和数据交互变得复杂时,Vuex可以通过集中式存储和管理,将状态的变化和交互逻辑统一管理,提高了代码的可维护性和可读性。

异步操作的处理:Vuex可以将异步操作进行集中处理,使得异步操作的流程更加清晰和易于管理,也可以避免异步操作带来的错误和风险。

用户状态的管理:Vuex可以用于管理用户的登录状态、个人信息等敏感信息,使得应用更加安全和稳定。

代码的组织和模块化:Vuex可以将复杂的业务逻辑和状态管理进行模块化,使得代码更加清晰、易于理解和维护。

可预测性和调试:Vuex提供了一种可预测的状态管理模式,可以清晰地了解状态的变化和流向,方便进行调试和测试。

16.Webpack打包流程。

首先会从配置文件和 Shell 语句中读取与合并参数,并初始化需要使用的插件和配置插件等执行环境所需要的参数;初始化完成后会调用Compiler的run来真正启动webpack编译构建过程,webpack的构建流程包括compilation(开始编译)、make(从入口点分析模块及其依赖的模块,创建这些模块对象 )、build(构建模块)、seal(封装构建结果)、emit阶段,执行完这些阶段就完成了构建过程。

image-20241206170815127

17.Webpack热更新

当代码文件修改并保存之后,webapck通过watch监听到文件发生变化,会对代码文件重新打包生成两个模块补丁文件manifest(js)和一个(或多个)updated chunk(js),将结果存储在内存文件系统中,通过websocket通信机制将重新打包的模块发送到浏览器端,浏览器动态的获取新的模块补丁替换旧的模块。

18.js事件循环机制

JS 主线程不断的循环往复的从任务队列中读取任务,执行任务,这种运行机制称为事件循环(event loop)

浏览器的事件循环(event loop)中分成宏任务和微任务。JS 中任务分成同步任务和异步任务。

  1. 宏任务(macro task)

JS 中主栈执行的大多数的任务,例如:定时器,事件绑定,ajax,回调函数,node中fs操作模块等就是宏任务

  1. 微任务(micro task)

promise, async/await, process.nextTick等就是微任务。

JS 中任务的执行顺序优先级是:主栈全局任务(宏任务) > 宏任务中的微任务 > 下一个宏任务。所以 promise(微任务) 的执行顺序优先级高于setTimeout定时器。

19.服务端渲染(SSR)

将组件在服务端直接渲染成 HTML 字符串,作为服务端响应返回给浏览器,最后在浏览器端将静态的 HTML“激活”(hydrate) 为能够交互的客户端应用。

优点:

更快的首屏加载:无需等到所有的 JavaScript 都下载并执行完成之后才显示。数据获取过程在首次访问时在服务端完成,相比于从客户端获取,可能有更快的数据库连接。

统一的心智模型:可以使用相同的语言以及相同的声明式、面向组件的心智模型来开发整个应用,而不需要在后端模板系统和前端框架之间来回切换。

更好的 SEO:搜索引擎爬虫可以直接看到完全渲染的页面。

20.Vue2和Vue3的区别

设计理念上:Vue2是基于Options API而Vue3是基于Composition API

模板编译上:Vue2是直接将模板全部编译成虚拟dom树,每次组件更新时,即使静态内容未发生变化,整个虚拟 DOM 树仍然会重新创建;而Vue3则采用了静态树提升和动态节点追踪机制,将这些静态内容提升到渲染函数的外部,只生成一次,标记动态节点,运行时仅更新这些真正需要变化的部分。

响应式系统上:Vue2是基于Object.defineProperty,通过递归劫持对象的每个属性来实现对数据访问和修改的拦截,但是无法监听新增或删除属性,因此添加了$set和$delete作为补充;Vue3是基于ES6的新特性Proxy的捕获器(trap)实现,捕获所有对对象的操作,劫持的是整个对象,而不是单个属性,这使得新增和删除属性、数组操作都能被监控,只有在访问嵌套对象时才会进行代理操作(懒代理)。

组件实现上:Vue2的组件是基于 Vue.extend 创建的,组件本质上是构造函数的实例;Vue3 的组件基于函数式实现,使用 setup 方法初始化逻辑,组件树的更新通过 Fragment 提升性能。

diff算法上:

Vue 2 使用的是基于递归的双指针,Vue 3 使用的是基于数组的动态规划。  Vue 2 会对整个组件树进行完整的遍历和比较; Vue 3的静态节点只会在首次渲染时被处理,后续更新时会跳过静态子树的比较,只对动态节点进行更新。  Vue 2 对于列表渲染(v-for)时的元素重新排序会比较低效,需要通过给每个元素设置唯一的 key 来提高性能;Vue 3在列表渲染时,通过跟踪元素的移动,可以更好地处理元素的重新排序。

生命周期上:Vue 2的beforeCreate和created在Vue 3被setup()替代,beforeDestroy变成了onBeforeUnmount(),destroyed变成了onUnmounted(),其他钩子则是变成了onXxxx的形式。

其他:

使用TS重写,提供了更好的TS支持

支持Tree-shaking

引入了Teleport组件

21.说说你对插槽的理解?

插槽本质上是对子组件的扩展,通过<slot>插槽向子组件内部指定位置传递内容,分为默认插槽、具名插槽和作用域插槽三种。

22.什么是重绘和重排

重排是指重新计算网页布局的过程,而重绘则是根据新的布局信息重新绘制网页的过程。它们的区别在于,重排会导致元素的尺寸、位置、内容等属性的变化,因此需要重新计算布局信息;而重绘则是在元素的位置和尺寸等属性不变的情况下,重新绘制元素的样式。

23.requestIdleCallback和requestAnimationFrame

当每秒绘制的帧数(FPS)达到 60 时,页面是流畅的,小于这个值时,用户会感觉到卡顿。

1s 60帧,所以每一帧分到的时间是 1000/60 ≈ 16 ms。所以我们书写代码时力求不让一帧的工作量超过 16ms。

一帧内需要完成如下六个步骤的任务:处理用户的交互

  • JS 解析执行

  • 帧开始。窗口尺寸变更,页面滚去等的处理

  • requestAnimationFrame(rAF)

  • 布局

  • 绘制

上面六个步骤完成后没超过 16 ms,说明时间有富余,此时就会执行 requestIdleCallback 里注册的任务。

即,requestAnimationFrame每一帧必定会执行,requestIdleCallback是捡浏览器空闲来执行任务。

假如浏览器一直处于非常忙碌的状态,requestIdleCallback 注册的任务有可能永远不会执行。

因为它发生在一帧的最后,此时页面布局已经完成,所以不建议在 requestIdleCallback 里再操作 DOM,这样会导致页面再次重绘。

24.useMemo和useCallback区别和使用场景

useMemo和useCallback都是用来优化性能的Hooks,作用都是通过判断依赖项是否改变来决定是否更新值。

区别是:

useMemo 缓存的结果是回调函数中return回来的值,主要用于缓存计算结果的值,通过减少不必要的复杂计算来优化性能

useCallback缓存的结果是函数,主要用于缓存函数,一般用于给子组件传递回调函数时,减少子组件的渲染次数,从而优化性能。需要注意的是,useCallback应该和React.memo配套使用,缺了一个都可能导致性能不升反而下降。

拓展点:

因为函数式组件每次任何一个state发生变化,会触发整个组件更新,一些函数是没有必要更新的,此时就应该缓存起来,提高性能,减少对资源的浪费。

25.http和https的区别

HTTP 的连接很简单,是无状态的,信息是明文传输;HTTPS 是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 HTTP 协议安全,但是也更耗费服务器资源,使用 HTTPS 协议需要到 CA申请证书。

HTTP 页面响应速度比 HTTPS 快,主要是因为 HTTP 使用 TCP 三次握手建立连接,客户端和服务器需要交换 3 个包,而 HTTPS除了 TCP 的三个包,还要加上 ssl 握手需要的 9 个包,所以一共是 12 个包。

HTTP 和HTTPS 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。

26.http的特点

工作于客户端/服务端的架构之上;

服务程序规模小而且通信速度很快;

允许传输任意类型的数据对象;

无连接:通信双方都不长久的维持对方的任何信息;

无状态:数据包的发送、传输和接收都是相互独立的。

27.Promise

Promise是异步编程的一种解决方案,比传统的解决方案(回调函数)更加合理和更加强大。

优点:解决了回调地狱的问题、更好地进行错误捕获、更适合处理一次性的结果

缺点:

当处于pending状态时无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

只能有一个最终值或一个最终错误,无法同时返回多个值

如果不设置回调函数,Promise内部抛出的错误,不会反应到外部

一旦新建它就会立即执行,无法中途取消

28.Vue和React的区别

React 是一个用于构建用户界面的 JavaScript 库,而 Vue 则是一个用于构建 Web 应用程序的 渐进式框架。

React采用的是单向数据流,需要手动setState;Vue是响应式数据驱动的设计思想,可以双向数据绑定,修改数据自动更新视图。

28.RESTful Api的优缺点

优点:

简单直观:RESTful 风格使用标准的 HTTP 方法(GET、POST、PUT、DELETE 等 )来对应资源的读取、创建、更新、删除操作。

可扩展性强

跨平台兼容性好

良好的缓存支持

客户端与服务器之间仅通过资源的表述来交互

缺点:

缺乏统一标准规范

版本管理复杂

安全问题:由于 RESTful 依赖 HTTP 协议,数据传输过程中如果没有额外加密(如 HTTPS),信息易被窃取篡改。而且 RESTful 接口难以直接实现复杂的权限控制与身份验证逻辑,往往要借助外部的鉴权、授权方案来补充。

不适用于实时性要求高的场景

 

 

拓展知识点

1.React Hooks

useState - 状态钩子

接收状态的初始值作为参数,返回一个数组,分别是状态值和更新该状态值的函数 。

const [text, setText] = useState('Hello, World');

useContext - 共享状态钩子

用于访问 React context 在组件树中传递的数据,而不必通过每个组件传递 props,接收一个Context对象,返回该给Context对象提供的状态。

// 父组件
// 使用 React Context API,在组件外部建立一个 Context。
const AppContext = React.createContext({});

// 父组件组件封装代码
// 通过AppContext.Provider将状态提供给子组件共享
<AppContext.Provider value={{
   username: 'superasome'
}}>
   <div className="App">
       <Navbar/>
       <Message/>
   </div>
</AppContext.Provide>

// 子组件中获取共享的状态 - 以Navbar组件为例
const Navbar = () => {
   const {username} = useContext(AppContext);
   return (
       <div className="navbar">
           <p>AwesomeSite</p>
           <p>{username}</p>
       </div>
  );
}

useReducer - action钩子

接受 Reducer 函数和状态的初始值作为参数,返回一个数组,分别是当前状态值和发送action的dispatch函数。

const [state, dispatch] = useReducer(reducer, initialState);

useEffect - 副作用钩子

用于执行副作用操作。第一个参数是一个回调函数;第二个参数是一个数组,用于给出Effect的依赖项。当第二个参数被省略时,每次组件渲染就会执行Effect。

useEffect(()  =>  {
 // Async Action
}, [dependencies])

useCallback

用于返回一个 memoized 版本的回调函数,防止不必要的渲染。第一个参数是回调函数,第二个参数是依赖数组。

const memoizedCallback = useCallback(
() => {
   // 回调函数体
},
[dependencies] // 依赖数组
);

useMemo

用于对计算结果进行记忆,避免在每次渲染时重复计算。第一个参数是回调函数,第二个参数是依赖数组。

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

useRef

useRef可以接受一个默认值,并返回一个含有current属性的可变对象; 使用场景: 1、获取子组件的实例(子组件需为react类继承组件); 2、获取组件中某个DOM元素; 3、用做组件的全局变量,useRef返回对象中含有一个current属性,该属性可以在整个组件色生命周期内不变,不会因为重复render而重复申明,类似于react类继承组件的属性this.xxx一样。原因:由于useState保存的变量会触发组件render,而使用useRef定义全局变量不会触发组件render;

const refContainer = useRef(initialValue);

useImperativeHandle

用于使用 ref 时暴露 DOM 元素的方法。

useImperativeHandle(ref, () => ({
 // 暴露的方法
}));

useLayoutEffect

与 useEffect 类似,但它在所有的 DOM 变更之后同步执行。这在需要读取 DOM 布局并同步触发重渲染时非常有用。

useLayoutEffect(() => {
 // 副作用操作
}, [dependencies]);

useDebugValue

用于在 React 开发者工具中显示自定义 hook 的标签。

useDebugValue(value);

2.React Native

posted @ 2025-01-13 14:34  何以之  阅读(17)  评论(0)    收藏  举报