Skip to content

1. JS原始类型有哪些?

JavaScript 中的原始类型包括:

  • Number:表示数值(整数和浮点数)。
  • String:表示文本字符串。
  • Boolean:表示布尔值(true 或 false)。
  • Null:表示空值,指明一个空对象引用。
  • Undefined:表示未定义的值,变量声明但未赋值时默认值为 undefined
  • Symbol:表示唯一的不可变值,通常用作对象属性的标识符。
  • BigInt:用于表示任意精度的整数。

2. 它们存在栈中还是堆里的呢?

原始类型的值是不可变的,并且由于它们大小固定、内存分配相对简单,通常存储在中。这使得它们的访问速度非常快。而引用类型(如对象、数组、函数)存储在堆中。

3. 引用类型有哪些?

引用类型包括:

  • Object:这是 JavaScript 中最基本的引用类型,包括普通对象(如 {})、数组(如 [])、函数(如 function () {})等。
  • Array:一种特殊类型的对象,用于存储有序的集合。
  • Function:也是一种特殊的对象,可以被调用。
  • Date:用于表示日期和时间。
  • RegExp:用于表示正则表达式。
  • MapSet:ES6 引入的集合类型。
  • WeakMapWeakSet:类似于 MapSet,但对键或元素是弱引用。

4. 他们为什么要去区分呢?都存在栈里面有没有问题?

区分原始类型和引用类型的主要原因是内存管理和性能

  • 原始类型的数据大小固定,通常很小,因此存储在栈中,这样可以提高访问速度。
  • 引用类型通常是复杂的数据结构,大小不固定,因此存储在堆中,栈中只存储它们的引用(即指针)。如果引用类型也存在栈中,可能会导致栈溢出,影响性能和稳定性。

5. 调用栈的数据是怎么回收的?堆的数据是怎么回收的?

  • 调用栈中的数据:栈是通过自动管理的。当函数执行完毕后,函数调用所占用的内存会自动释放。因此,栈中的数据回收不需要手动干预,由系统自动管理。

  • 堆中的数据:堆内存由垃圾回收机制(如 JavaScript 引擎的垃圾回收器)管理,主要采用标记-清除引用计数等算法。当一个对象不再被引用时,它占用的内存将被标记为可回收,垃圾回收器会在合适的时间回收这些内存。

6. ES Module 和 CommonJS区别

  • ES Module

    • 语法:importexport
    • 模块是静态加载的,即在编译时解析依赖关系,确定模块导入导出。
    • 是原生支持的模块系统,浏览器和 Node.js 12+ 版本都支持。
    • 支持树摇(Tree Shaking)优化未使用的代码。
  • CommonJS

    • 语法:requiremodule.exports
    • 模块是动态加载的,依赖关系在运行时解析。
    • 主要用于 Node.js 环境中,浏览器中需借助工具(如 Browserify、Webpack)转换。
    • 不支持树摇优化,因为模块加载是动态的。

7. 浏览器事件循环机制

浏览器的事件循环机制用于管理异步操作的执行顺序。主线程执行同步代码,异步任务(如 setTimeoutPromise)推送到任务队列,主线程空闲时从任务队列中取出任务执行。事件循环(Event Loop)不断检查调用栈和任务队列的状态,确保异步代码能在正确的时间点被执行。

8. 宏任务和微任务是什么?哪些属于宏任务哪些属于微任务?

  • 宏任务:包括 script(整体代码)、setTimeoutsetIntervalI/O 操作等。宏任务执行完后,事件循环会查看微任务队列并执行所有微任务,然后再执行下一个宏任务。

  • 微任务:包括 Promise.thenMutationObserverqueueMicrotask。微任务在当前宏任务结束后、下一个宏任务开始前执行。

9. HTTP1和HTTP2的区别

  • HTTP1

    • 每个请求都需要单独的 TCP 连接,导致性能瓶颈。
    • 头部冗余,每次请求都会重复发送相同的头部信息。
  • HTTP2

    • 支持多路复用,一个连接上可以发送多个请求,极大地减少了延迟。
    • 头部压缩,减少数据传输量。
    • 服务端推送,服务器可以主动将资源推送到客户端。

10. 多路复用是什么?主要解决什么问题?

多路复用允许在一个 TCP 连接上同时发送多个 HTTP 请求,解决了 HTTP/1.1 中“每个请求需要一个连接”的问题,减少了连接的建立和管理开销,提高了传输效率。

11. 浏览器缓存机制有哪些?

  • 强缓存:利用 ExpiresCache-Control 头部实现,命中后直接从缓存读取,避免请求发到服务器。
  • 协商缓存:利用 Last-ModifiedETag 头部字段,缓存过期后由客户端向服务器验证资源是否更新,未更新则继续使用缓存。

12. 讲一下React虚拟DOM

React 的虚拟 DOM 是对真实 DOM 的抽象表示。通过虚拟 DOM,可以在内存中对 DOM 进行快速更新,然后将差异(diff)应用到实际的 DOM 上,减少直接操作 DOM 的性能开销,提高渲染效率。

13. 讲一下React的diff算法

React 的 diff 算法主要用于比较两棵虚拟 DOM 树的差异。算法假设同一层级下的元素类型相同,逐层比较节点,采用分而治之的策略,(dfs深搜)大大减少了复杂度。对于列表类型的组件,通过 key 属性来识别节点,避免不必要的重渲染。

14. React有哪些Hooks?

  • useState:用于在函数组件中添加状态。
  • useEffect:用于处理副作用(如数据获取、订阅等)。
  • useContext:用于在组件树中传递和接收上下文数据。
  • useReducer:用于复杂的状态逻辑处理。
  • useMemo:用于性能优化,避免不必要的计算。
  • useCallback:用于性能优化,避免不必要的函数重新创建。
  • useRef:用于获取 DOM 节点或保存任意变量的引用。
  • useImperativeHandle:定制 useRef 暴露给父组件的实例值。
  • useLayoutEffect:与 useEffect 类似,但它会在所有 DOM 变更之后同步触发。
  • useDebugValue:用于自定义 Hook 在 React 开发者工具中的标签。

15. React Hooks和高阶组件有什么区别?

  • React Hooks:允许在函数组件中使用状态和其他 React 特性,代码更加简洁和模块化,不会增加组件嵌套的复杂度。
  • 高阶组件(HOC):是一种模式,用于重用组件逻辑,通过将一个组件传递给高阶组件,返回一个增强版组件。它增加了组件嵌套的复杂度,可能导致“Wrapper Hell”。

16. 高阶组件有哪些?

常见的高阶组件包括:

  • withRouter:将路由相关的 props 注入到组件中。
  • connect:在 React-Redux 中使用,将 Redux 状态和操作注入到组件中。
  • withAuth:自定义的高阶组件,通常用于鉴权,限制访问权限。

17. ES6 map和weakmap的区别

  • Map

    • 键值对集合,键可以是任意类型(对象、原始值)。
    • 强引用,键不会被垃圾回收。
  • WeakMap

    • 键必须是对象,值可以是任意类型。
    • 弱引用,键可以被垃圾回收,因此更适合存储临时对象。

18. WeakMap的常见使用场景

WeakMap 的常见使用场景主要集中在需要对对象进行弱引用管理的地方。

  1. 缓存和数据存储
  • WeakMap 可以用来存储与对象关联的数据,而不影响垃圾回收。当对象不再被引用时,WeakMap 中与之关联的数据也会被自动清除,避免内存泄漏。
  • 示例:在缓存计算结果、存储对象元数据时,使用 WeakMap 来避免内存泄漏。
javascript
const cache = new WeakMap();

function compute(obj) {
    if (!cache.has(obj)) {
        // 进行计算并存储结果
        const result = /* 计算逻辑 */;
        cache.set(obj, result);
    }
    return cache.get(obj);
}
  1. DOM 节点关联数据

    • WeakMap 非常适合用来将 DOM 节点与数据关联起来。因为 DOM 节点可能会被删除,WeakMap 能够确保这些节点不再使用时,相关联的数据可以被自动回收。
    • 示例:为每个 DOM 元素存储事件处理器或其他相关数据。
    javascript
    const elementData = new WeakMap();
    
    function bindDataToElement(element, data) {
        elementData.set(element, data);
    }
    
    function getDataFromElement(element) {
        return elementData.get(element);
    }
  2. 隐藏私有数据

  • WeakMap 可以用作对象的“私有”属性存储,使得这些数据无法通过对象本身直接访问,提供了一种隐藏私有数据的方式。
  • 示例:在类中使用 WeakMap 存储实例的私有属性。
javascript
const privateData = new WeakMap();

class MyClass {
    constructor(value) {
        privateData.set(this, { value });
    }

    getValue() {
        return privateData.get(this).value;
    }
}

const instance = new MyClass(42);
console.log(instance.getValue()); // 42
  1. 用于管理弱引用的监听器或回调
  • WeakMap 可以用于管理与对象相关的监听器或回调函数,这样当对象不再需要时,相关的监听器或回调可以被自动清除。
javascript
const listeners = new WeakMap();

function addListener(obj, listener) {
    listeners.set(obj, listener);
}

function trigger(obj) {
    const listener = listeners.get(obj);
    if (listener) listener();
}

总结:

WeakMap 的关键特性是它对键的弱引用,适用于存储那些与对象相关但不需要强引用的值,这样可以有效避免内存泄漏,特别适合在缓存、DOM 操作和隐藏私有数据等场景中使用。

古茗二面

1. TypeScript 设计原理 vs JavaScript 设计原理

JavaScript 设计原理

  • 动态类型:JavaScript 是一种动态类型语言,变量类型在运行时确定。虽然灵活,但容易引发类型错误和不确定性。
  • 解释执行:JavaScript 通过解释器直接运行在浏览器或 Node.js 环境中,代码按行解释执行,具有即时性和动态性。
  • 弱类型检查:由于没有严格的类型约束,类型转换和类型错误时有发生,这可能导致运行时错误。
  • 原型继承:JavaScript 采用基于原型的继承机制,与传统的类继承不同,所有对象都可以直接或间接地继承自其他对象。
  • 单线程与异步:JavaScript 是单线程运行的,但通过事件循环机制可以处理异步任务,如回调、Promise、async/await。

TypeScript 设计原理

  • 静态类型:TypeScript 增加了静态类型系统,在编译时进行类型检查,确保类型安全,减少运行时错误。
  • 类型推断:TypeScript 能够根据上下文自动推断变量的类型,减少显式类型声明的负担,同时提供类型安全保障。
  • 面向对象特性:TypeScript 引入了基于类的继承、接口、抽象类、泛型等面向对象编程特性,增强了代码的组织性和可维护性。
  • 兼容性:TypeScript 是 JavaScript 的超集,兼容所有 JavaScript 代码,并能通过编译器生成纯 JavaScript 代码。
  • 增强开发体验:通过静态类型和代码提示,TypeScript 提高了开发效率,减少了调试和维护的时间成本。

2. Vite vs Webpack 深入对比

Webpack 设计原理与特点

  • 模块打包:Webpack 是一个模块打包工具,可以处理 JavaScript、CSS、图片等多种资源文件,并将其打包成一个或多个 bundle 文件,减少 HTTP 请求数,提升页面加载性能。
  • 依赖图:Webpack 构建项目时,会从入口文件开始,递归地解析所有依赖,生成一个依赖图(Dependency Graph),并根据图中的关系进行打包。
  • 多功能插件:Webpack 提供了强大的插件系统,几乎可以拦截打包过程中的每一个环节,开发者可以通过插件扩展 Webpack 的功能,如代码压缩、热模块替换(HMR)、静态资源优化等。
  • 灵活配置:Webpack 的配置文件 webpack.config.js 允许用户进行高度定制,可以根据项目需求配置各种打包策略。
  • 慢启动,快构建:由于 Webpack 需要分析整个依赖图,因此在大型项目中,初次构建时间可能较长,但随着增量构建和持久缓存策略的引入,后续的构建速度会显著加快。

Vite 设计原理与特点

  • ES Modules 原生支持:Vite 采用浏览器原生支持的 ES Modules 进行开发阶段的模块加载,不需要对所有文件进行打包,避免了 Webpack 的繁重构建过程。
  • 极速启动:Vite 只在需要时按需编译文件,因此启动速度极快,特别适合大型项目的开发环境。
  • 基于 Rollup 打包:在生产环境中,Vite 通过 Rollup 进行打包,生成优化后的静态文件。Rollup 专注于高效打包和代码拆分,生成的包体积更小。
  • 内置优化:Vite 内置了许多优化策略,如依赖预构建、按需加载、模块热替换(HMR)等,开发体验流畅。
  • 插件系统:Vite 也支持插件,插件基于 Rollup 实现,可以与 Rollup 生态中的许多插件共享。

对比总结

  • 启动速度:Vite 的启动速度远快于 Webpack,特别是在开发阶段,Vite 的体验更加流畅。
  • 打包方式:Webpack 是一切皆模块的打包工具,而 Vite 更依赖于浏览器原生的模块系统,生产环境则借助 Rollup 打包,二者在打包策略上有本质区别。
  • 复杂度:Webpack 提供了更为复杂和全面的配置能力,适用于复杂的企业级项目;Vite 则更注重简洁性和开发效率,适合快速开发。
  • 生态系统:Webpack 拥有广泛的插件和 loader 生态,能处理几乎所有前端开发需求;Vite 则借助 Rollup 插件系统,并逐步形成自己的生态。

3. 前端工程化

概念与背景

前端工程化是指在前端开发中引入工程化思维和工具,以提高开发效率、代码质量和团队协作能力。随着前端技术栈的复杂化,前端工程化变得越来越重要。

核心要素

  • 模块化开发:将代码拆分为独立模块,通过模块化管理依赖关系,增强代码的可维护性和复用性。常用的模块化工具包括 Webpack、Rollup、Vite 等。
  • 自动化构建:通过自动化工具(如 Webpack、Gulp、Grunt)实现代码的打包、压缩、转换等操作,减少手动操作,提升效率。
  • 版本控制:通过 Git 等版本控制系统管理代码变更,支持团队协作和回滚操作。
  • 持续集成与部署(CI/CD):通过 Jenkins、Travis CI 等工具自动化构建、测试和部署流程,确保代码的稳定性和快速发布。
  • 代码规范与静态检查:使用 ESLint、Prettier 等工具进行代码规范检查和格式化,保证代码一致性和可读性。
  • 测试:通过单元测试、集成测试等保障代码的正确性和稳定性。Jest、Mocha、Cypress 是常用的测试工具。
  • 文档化与自动化工具:使用工具(如 Storybook、Docusaurus)生成组件文档和 API 文档,确保团队对项目的理解一致。
  • 性能优化:通过代码分割、资源压缩、缓存策略、CDN 等手段优化前端性能,提升用户体验。
  • 监控与分析:通过前端监控工具(如 Sentry、Google Analytics)监控用户行为和异常,分析性能瓶颈。

前端工程化的典型工具链

  • 开发阶段:Vite、Webpack、Babel、TypeScript
  • 代码管理:Git、GitHub/GitLab
  • 自动化构建:Webpack、Rollup、Gulp
  • 代码规范:ESLint、Prettier、Stylelint
  • 测试:Jest、Mocha、Chai、Cypress
  • 持续集成/部署:Jenkins、Travis CI、CircleCI
  • 文档生成:Storybook、Docusaurus、Typedoc
  • 性能优化:Lighthouse、Webpack Bundle Analyzer
  • 监控与分析:Sentry、LogRocket、Google Analytics

总结

前端工程化是一套涵盖开发、测试、部署、优化、监控等各个环节的系统性方案,目标是提升开发效率、降低出错率、保证项目质量。通过合理的工程化实践,团队可以更高效地协作,快速响应需求变化,并且保持代码库的健康状态。

Copyright © 2024-present LofiSu