前端面试第 77 期 - 2025.09.02 更新前端面试问题总结(15 道题)

2025.07.06 - 2025.08.31 更新前端面试问题总结(15 道题)

获取更多面试相关问题可以访问

github 地址: github.com/pro-collect...

gitee 地址: gitee.com/yanleweb/in...

目录

中级开发者相关问题【共计 9 道题】

  1. 深层清理对象中的空值属性【热度: 234】【代码实现/算法】
  2. 介绍一下 git stash【热度: 386】【web 应用场景】
  3. JS 里面, 对于对象的读写, 是使用 object 好,还是 Map,性能差异如何?【热度: 610】【JavaScript】【出题公司: 阿里巴巴】
  4. less 与 scss 有何区别【热度: 61】【web 应用场景】【出题公司: 腾讯】
  5. less 与 css 有何区别【热度: 214】【web 应用场景】【出题公司: 腾讯】
  6. 用 css 实现一个 loading 动画, 该如何做(转圈)【热度: 180】【CSS】
  7. ts 有哪些常用的关键词【热度: 178】【TypeScript】【出题公司: 美团】
  8. 对比一下 ts 和 jsdoc【热度: 126】【TypeScript】
  9. react 开发的应用里面, 如何给系统设置一个全局的崩溃的提示页面【热度: 725】【web 框架】【出题公司: 小米】

高级开发者相关问题【共计 6 道题】

  1. 将网页 dom 元素转为图片, 有哪些办法【热度: 41】【web 应用场景】
  2. 介绍一下 git diff【热度: 396】【web 应用场景】
  3. less 是否支持条件判定【热度: 112】【web 应用场景】【出题公司: 腾讯】
  4. less 有那些高级特性, 驱使你们项目工程去使用它【热度: 336】【web 应用场景】【出题公司: 腾讯】
  5. ts 里面 infer 是什么关键词, 怎么用【热度: 975】【TypeScript】【出题公司: 美团】
  6. TypeScript 中,ReturnType 的作用和用法【TypeScript】

中级开发者相关问题【共计 9 道题】

1123. 深层清理对象中的空值属性【热度: 234】【代码实现/算法】

请实现一个函数 deepOmitNil,要求如下:

  1. 功能:递归处理一个可能包含嵌套结构的对象(或数组),移除所有层级中值为 nullundefined 的属性

  2. 要求:

    • 支持对象和数组的嵌套结构
    • 对于对象:移除值为 null/undefined 的属性,保留其他属性并继续递归处理属性值
    • 对于数组:递归处理每个元素,同时过滤掉值为 null/undefined 的元素
    • 不改变原始数据结构,返回处理后的新数据
  3. 示例:

    javascript 复制代码
    // 输入
    const data = {
      name: 'test',
      age: null,
      info: {
        address: undefined,
        contact: {
          phone: null,
          email: 'test@example.com'
        }
      },
      list: [1, null, { id: undefined, value: 2 }, undefined, 3]
    };
    
    // 输出
    {
      name: 'test',
      info: {
        contact: {
          email: 'test@example.com'
        }
      },
      list: [1, { value: 2 }, 3]
    }

请用 JavaScript 实现该函数,可使用 Lodash 工具库辅助开发。

解法

javascript 复制代码
function deepOmitNil(value) {
  // 处理null和undefined的情况
  if (value === null || value === undefined) {
    return null;
  }

  // 处理数组:递归处理每个元素,并过滤掉null和undefined
  if (Array.isArray(value)) {
    return value.map((item) => deepOmitNil(item)).filter((item) => item !== null && item !== undefined);
  }

  // 处理普通对象:检查是否为纯粹的对象(排除数组、null等)
  if (Object.prototype.toString.call(value) === "[object Object]") {
    const result = {};
    // 遍历对象自身属性
    for (const key in value) {
      if (value.hasOwnProperty(key)) {
        const processedValue = deepOmitNil(value[key]);
        if (processedValue !== null && processedValue !== undefined) {
          result[key] = processedValue;
        }
      }
    }
    return result;
  }

  // 其他类型直接返回(如字符串、数字、布尔值等)
  return value;
}

// 示例用法
const data = {
  name: "test",
  age: null,
  info: {
    address: undefined,
    contact: {
      phone: null,
      email: "test@example.com",
    },
  },
  list: [1, null, { id: undefined, value: 2 }, undefined, 3],
};

console.log(deepOmitNil(data));

1124. 介绍一下 git stash【热度: 386】【web 应用场景】

关键词:git stash

git stash 是 Git 中一个非常实用的命令,用于临时保存工作区和暂存区的修改,让你可以在不提交当前变更的情况下,切换到其他分支或进行其他操作,之后还能恢复这些临时保存的变更。

核心作用

当你正在一个分支上开发,突然需要切换到其他分支(比如修复紧急 Bug),但当前工作还没完成不想提交时,git stash 可以:

  • 把工作区和暂存区的所有修改(包括新增、修改、删除的文件)暂存到一个"栈"中
  • 让工作区回到最近一次提交的干净状态(与 git reset --hard HEAD 效果类似,但变更被临时保存了)
  • 之后可以随时从"栈"中恢复这些变更,继续之前的工作

常用命令

  1. 暂存当前变更

    bash 复制代码
    git stash
    # 或添加描述(推荐,方便区分多个stash)
    git stash save "描述信息:例如「首页导航栏修改」"

    执行后,工作区会恢复到干净状态,变更被存入 stash 栈。

  2. 查看所有暂存的变更

    bash 复制代码
    git stash list

    输出类似:

    kotlin 复制代码
    stash@{0}: On feature/login: 修复登录按钮样式
    stash@{1}: On develop: 临时添加调试日志

    stash@{n} 是每个暂存的唯一标识,n 越小表示越新。

  3. 恢复暂存的变更

    • 恢复最新的 stash(stash@{0}),且保留 stash 记录:

      bash 复制代码
      git stash apply
    • 恢复指定的 stash(例如 stash@{1}):

      bash 复制代码
      git stash apply stash@{1}
    • 恢复最新的 stash 并删除该 stash 记录(推荐用完即删的场景):

      bash 复制代码
      git stash pop
  4. 删除暂存的变更

    • 删除最新的 stash:

      bash 复制代码
      git stash drop
    • 删除指定的 stash:

      bash 复制代码
      git stash drop stash@{1}
    • 删除所有 stash:

      bash 复制代码
      git stash clear
  5. 查看 stash 中的具体修改

    查看最新 stash 与当前工作区的差异:

    bash 复制代码
    git stash show

    查看详细差异(显示具体修改的内容):

    bash 复制代码
    git stash show -p

注意事项

  • git stash 只会暂存已跟踪文件 (即已被 Git 管理的文件)的修改,以及已添加到暂存区的新增文件 。未跟踪的全新文件(未执行过 git add)不会被暂存,需要先执行 git add 或使用 git stash -u-u 表示包括未跟踪文件)。
  • 恢复 stash 时,如果当前工作区有修改,可能会出现冲突,需要手动解决。
  • stash 存储在本地仓库,不会被推送到远程,切换电脑后无法获取。

简单来说,git stash 就像一个"剪贴板",让你可以临时"剪切"当前工作状态,稍后再"粘贴"回来,非常适合处理多任务切换的场景。

1126. JS 里面, 对于对象的读写, 是使用 object 好,还是 Map,性能差异如何?【热度: 610】【JavaScript】【出题公司: 阿里巴巴】

关键词:Object 与 Map

在 JavaScript 中,ObjectMap 都可以用于存储键值对,但它们的设计目标和性能特性存在差异,选择哪一种取决于具体使用场景。

核心差异与性能对比

1. 内存占用
  • Object

    本质是原型链继承的对象,会默认一些额外属性(如 __proto__constructor),且键只能是字符串或 Symbol。

    对于少量键值对,内存开销较小,但键名会被强制转换为字符串(如数字 1 会转为 "1")。

  • Map

    专为键值对存储设计,无原型链开销,键可以是任意类型(包括对象、函数等)。

    但内部实现会维护哈希表结构,存储相同数量的键值对时,内存占用通常比 Object 略高(尤其键值对较少时)。

2. 读写性能
  • Object

    • 读取/写入速度:对于静态键(提前确定的键名),访问速度极快,因为 JavaScript 引擎会对对象属性进行优化(如静态属性的偏移量缓存)。
    • 动态键场景 :如果键名是动态生成的(如通过变量拼接),性能会略有下降(需哈希计算),但仍优于 Map 对非字符串键的处理。
  • Map

    • 读取/写入速度:对于频繁的增删改查(尤其是动态键或非字符串键),性能更稳定。
    • 优势体现在:键可以是任意类型(无需转换)、内部哈希表优化更适合高频动态操作。
    • 劣势:对于静态字符串键,访问速度通常比 Object 慢 10%-30%(不同引擎优化不同)。
3. 遍历性能
  • Object

    遍历需要先获取键名(Object.keys() 等),再迭代访问,步骤较多。

    且会遍历自身可枚举属性(需注意原型链污染问题),额外消耗性能。

  • Map

    原生支持迭代器(for...of 直接遍历),遍历速度通常比 Object 快,尤其是键值对数量较多时。

    Mapsize 属性可直接获取长度(Object 需要 keys().length 计算),更高效。

4. 极端场景测试
  • 小规模数据(<100 键值对)
    Object 性能略优,内存占用更低,适合简单配置、数据存储。

  • 大规模数据(>1000 键值对)
    Map 在频繁增删、动态键、遍历场景下性能更稳定,Object 可能因哈希冲突导致性能波动。

  • 非字符串键
    Map 优势明显(无需转换键类型),Object 需要手动处理键名转换(如将对象转为字符串标识),既麻烦又影响性能。

使用建议

场景 推荐选择 理由
静态键名(如 { name: 'a' } Object 访问速度快,语法简洁,适合数据结构固定的场景(如配置、DTO)。
动态键名(如变量作为键) Map 无需处理键名转换,增删改查性能更稳定。
非字符串键(对象、函数等) Map 原生支持任意类型键,Object 会强制转换键为字符串,可能导致冲突。
频繁增删或遍历 Map 迭代器优化更好,size 属性获取高效,适合缓存、集合类场景。
序列化需求(JSON.stringify Object Map 无法直接序列化,需手动转换为对象,Object 原生支持。

总结

  • Object 适合静态、简单的键值对存储,语法简洁,内存占用低,静态访问速度快。
  • Map 适合动态、复杂的键值对场景(尤其是非字符串键、高频增删遍历),性能更稳定。

性能差异在大多数业务场景中不明显,优先根据代码可读性和功能需求选择,极端性能敏感场景(如大数据处理)再针对性优化。

1129. less 与 scss 有何区别【热度: 61】【web 应用场景】【出题公司: 腾讯】

关键词:less 与 scss

Less 和 Sass(通常以 SCSS 语法使用)是最流行的两款 CSS 预处理器,都旨在解决原生 CSS 的局限性(如缺乏变量、嵌套、复用机制等),但在语法细节、功能设计和生态上存在诸多差异。以下从核心区别、使用场景等方面详细对比:

一、语法差异

这是最直观的区别,直接影响开发体验。

特性 Less SCSS(Sass 的新语法)
语法风格 更接近原生 CSS,无需强制使用分号和大括号 完全兼容 CSS 语法,必须使用分号和大括号
变量声明 @variable: value; $variable: value;
嵌套规则 支持,与 SCSS 类似 支持,与 Less 类似
注释 单行 //(编译后移除)和多行 /* */(保留) 同 Less
示例代码 less<br>.container {<br> color: @text-color;<br> .box { padding: 10px }<br>}<br> scss<br>.container {<br> color: $text-color;<br> .box { padding: 10px; }<br>}<br>

关键区别

  • Less 语法更灵活,允许省略分号和大括号(类似 Stylus),但通常推荐保留以保持一致性;
  • SCSS 严格要求分号和大括号,完全兼容 CSS,因此从 CSS 迁移到 SCSS 几乎零成本。

二、变量与作用域

两者都支持变量,但作用域规则和特性有差异。

  1. 变量符号

    • Less 用 @(如 @color: red;);
    • SCSS 用 $(如 $color: red;),避免与 CSS 原生 @ 规则(如 @media)冲突。
  2. 作用域行为

    • Less :变量遵循「延迟加载」(Lazy Loading),即变量在使用前无需声明,作用域内后定义的变量会覆盖先定义的。

      less 复制代码
      .box {
        color: @color; // 允许使用后定义的变量
        @color: red;
      }
    • SCSS :变量必须先声明后使用,作用域更严格(类似 JavaScript)。

      scss 复制代码
      .box {
        color: $color; // 报错:$color 未定义
        $color: red;
      }
  3. 全局变量

    • SCSS 需用 !global 关键字显式声明全局变量(局部作用域中):

      scss 复制代码
      .box {
        $color: red !global; // 声明为全局变量
      }
      .text {
        color: $color;
      } // 可访问
    • Less 中变量默认全局有效(局部变量会覆盖全局,但不会污染全局)。

三、混合(Mixins)与函数

两者都支持代码复用,但实现方式和功能有差异。

1. 混合(Mixins)

用于复用样式片段。

  • Less

    混合无需特殊关键字,直接定义类或 id 选择器,使用时加括号(可选):

    less 复制代码
    // 定义混合
    .border-radius(@radius: 4px) {
      border-radius: @radius;
    }
    // 使用混合(可省略括号)
    .btn {
      .border-radius; // 或 .border-radius(8px)
    }
  • SCSS

    混合必须用 @mixin 定义,用 @include 调用,语法更明确:

    scss 复制代码
    // 定义混合
    @mixin border-radius($radius: 4px) {
      border-radius: $radius;
    }
    // 使用混合
    .btn {
      @include border-radius(8px);
    }
2. 函数(Functions)

用于计算值并返回结果(不直接生成样式)。

  • Less :函数功能较弱,主要依赖内置函数(如 darken()lighten()),自定义函数需通过混合模拟(不支持返回值)。

  • SCSS :支持用 @function 自定义函数,可返回值,功能更强大:

    scss 复制代码
    // 自定义函数:计算百分比宽度
    @function col-width($n) {
      @return ($n / 12) * 100%;
    }
    .col-6 {
      width: col-width(6); // 50%
    }

四、条件与循环

处理动态逻辑的能力不同。

1. 条件判断
  • Less :通过 when 关键字实现条件(Guards),语法较特殊:

    less 复制代码
    .theme(@type) when (@type = "dark") {
      background: #333;
    }
    .box {
      .theme("dark");
    }
  • SCSS :支持 @if/@else 语句,更接近传统编程语言:

    scss 复制代码
    @mixin theme($type) {
      @if $type == "dark" {
        background: #333;
      } @else {
        background: #fff;
      }
    }
    .box {
      @include theme("dark");
    }
2. 循环
  • Less:通过混合自调用实现循环,语法较繁琐:

    less 复制代码
    .loop(@n) when (@n > 0) {
      .item-@{n} {
        width: @n * 10px;
      }
      .loop(@n - 1);
    }
    .loop(3); // 生成 .item-3、.item-2、.item-1
  • SCSS :提供 @for/@each/@while 多种循环语法,更直观:

    scss 复制代码
    // @for 循环
    @for $i from 1 through 3 {
      .item-#{$i} {
        width: $i * 10px;
      }
    }

五、模块化与导入

处理样式文件拆分的方式。

  • Less

    • @import "file.less"; 导入文件,支持条件导入(结合 when):

      less 复制代码
      @import "theme.less" when (@theme = "dark");
    • 无内置模块化机制,需通过工具(如 Webpack)实现按需加载。

  • SCSS

    • @import "file.scss"; 导入文件,支持嵌套导入(在选择器内导入,作用域受限):

      scss 复制代码
      .box {
        @import "partial.scss"; // 仅在 .box 内生效
      }
    • 支持 @use@forward(Sass 3.8+),实现更严格的模块化(类似 ES6 模块),避免变量冲突:

      scss 复制代码
      // 导入并命名空间
      @use "variables" as vars;
      .box {
        color: vars.$text-color;
      }

六、生态与工具链

  • SCSS

    • 由 Ruby 开发(后部分用 C 重写),但现在主流通过 dart-sass 编译(性能更好);
    • 生态更成熟,广泛用于 React、Vue 等框架的组件库(如 Ant Design、Bootstrap 4+);
    • 工具支持完善(如 VS Code 的 Sass 插件、Webpack 的 sass-loader)。
  • Less

    • 基于 JavaScript 开发,编译速度快(尤其在 Node.js 环境);
    • 生态相对较小,但在早期前端框架(如 Bootstrap 3)中广泛使用;
    • 工具支持同样完善(如 less-loader)。

七、选择建议

场景/需求 推荐选择 理由
从 CSS 平滑迁移 SCSS 语法完全兼容 CSS,学习成本低
需要强大的函数和逻辑能力 SCSS 支持 @function@if/@for 等,适合复杂计算和动态样式
追求简洁语法和快速上手 Less 语法更灵活,变量和混合使用更简单
大型项目/组件库开发 SCSS 模块化机制(@use/@forward)更规范,避免变量冲突
依赖 JavaScript 生态(如 Node.js) Less 基于 JS 开发,与 Node 工具链集成更自然
团队已有 CSS 代码库 SCSS 无需修改原有 CSS 语法即可直接使用

总结

  • Less 更轻量、语法更灵活,适合中小型项目或追求简单上手的场景;
  • SCSS 功能更强大、语法更规范,适合大型项目或需要复杂逻辑的场景,且生态更成熟。

两者核心功能(变量、嵌套、混合)重叠度高,选择时可根据团队熟悉度、项目规模和工具链兼容性决定。

1130. less 与 css 有何区别【热度: 214】【web 应用场景】【出题公司: 腾讯】

关键词:less 与 css

Less 是 CSS 的预处理器,它在原生 CSS 的基础上扩展了诸多功能,解决了原生 CSS 开发中的痛点(如缺乏变量、复用机制、逻辑处理等)。以下是 Less 与 CSS 的核心区别:

1. 语法与功能

特性 CSS(原生) Less
变量支持 无(CSS 变量 --var 是较新特性,兼容性和功能有限) 支持 @variable: value 定义变量,可全局复用、参与运算,优先级清晰
嵌套规则 需重复书写父选择器,代码冗余(如 .parent .child { ... } 支持选择器嵌套(类似 HTML 结构),减少重复代码,层级更清晰
代码复用 无原生复用机制,需复制粘贴或依赖 @import 导入整个文件 支持「混合(Mixin)」复用样式片段,可带参数实现个性化复用
逻辑处理 无(仅能通过 @media 实现简单条件,无循环、判断) 支持条件判断(when)、循环(混合自调用)、运算(加减乘除、颜色计算)
模块化 @import 导入整个 CSS 文件,增加请求数,无作用域隔离 支持导入 Less 文件,可结合 (reference) 仅引用变量/混合,避免冗余代码
内置工具 提供颜色处理(darken()lighten())、数值计算(percentage())等函数

2. 开发效率

  • CSS

    编写重复代码多(如相同颜色、尺寸需多次书写),修改时需全局搜索替换,维护成本高。

    示例(重复代码问题):

    css 复制代码
    .btn {
      background: #1890ff;
    }
    .card {
      border-color: #1890ff;
    }
    .title {
      color: #1890ff;
    }
    /* 若要修改主题色,需逐个修改 */
  • Less

    通过变量、混合等特性减少重复代码,修改时只需调整一处,开发和维护效率大幅提升。

    示例(解决重复问题):

    less 复制代码
    @primary: #1890ff; /* 变量定义 */
    .btn {
      background: @primary;
    }
    .card {
      border-color: @primary;
    }
    .title {
      color: @primary;
    }
    /* 修改主题色只需改 @primary 即可 */

3. 编译方式

  • CSS

    是浏览器可直接解析的样式语言,无需编译,写完即可运行。

  • Less

    是「预编译语言」,浏览器无法直接识别,必须通过 Less 编译器(如 lessc、Webpack 的 less-loader)转换为 CSS 后才能被浏览器解析。

    流程:Less 代码 → 编译 → CSS 代码 → 浏览器执行

4. 代码结构

  • CSS

    层级嵌套需通过后代选择器实现,代码冗长且层级不直观。

    示例:

    css 复制代码
    .header {
      padding: 20px;
    }
    .header .logo {
      width: 100px;
    }
    .header .nav {
      margin-left: 20px;
    }
    .header .nav li {
      display: inline-block;
    }
  • Less

    支持嵌套语法,结构与 HTML 一致,层级清晰,减少选择器冗余。

    示例:

    less 复制代码
    .header {
      padding: 20px;
      .logo {
        width: 100px;
      }
      .nav {
        margin-left: 20px;
        li {
          display: inline-block;
        }
      }
    }

5. 适用场景

  • CSS

    适合简单页面(如静态页面、小网站),或需要快速编写、无需复杂逻辑的场景。

  • Less

    适合中大型项目、组件库开发或需要频繁复用样式、动态调整主题的场景(如通过变量切换主题色、响应式适配)。

总结

Less 不是替代 CSS,而是对 CSS 的增强:

  • CSS 是基础:浏览器最终执行的是 CSS,它是样式的"目标语言"。
  • Less 是工具:通过提供变量、嵌套、复用等功能,让开发者更高效地编写 CSS,最终仍需编译为 CSS 才能运行。

简单说,Less 解决了原生 CSS"写起来麻烦、改起来痛苦"的问题,是现代前端开发中提升样式开发效率的主流选择。

1131. 用 css 实现一个 loading 动画, 该如何做(转圈)【热度: 180】【CSS】

关键词:css 动画

作者备注

这个问题主要是对 css 动画的考察, 比直接问 animation 和 transform 属性有意义。

可以利用 CSS 的 animation 和 transform 属性,通过旋转一个带有渐变边框的元素来实现。

这个转圈 loading 动画的核心实现思路如下:

  1. 创建圆形元素

    • 使用 widthheight 设置相同的尺寸
    • 通过 border-radius: 50% 将方形元素变成圆形
  2. 设计边框样式

    • 设置一个较粗的边框(border
    • 让大部分边框保持半透明(rgba(255, 255, 255, 0.3)
    • 只让顶部边框使用实色(border-top-color),形成旋转时的流动效果
  3. 添加旋转动画

    • 定义 spin 动画,通过 transform: rotate(360deg) 实现 360 度旋转
    • 使用 animation 属性应用动画,设置 1s 为一个周期,ease-in-out 缓动效果,infinite 无限循环

直接上代码

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>CSS Loading Spinner</title>
    <style>
      /* 基础样式设置 */
      body {
        margin: 0;
        padding: 0;
        min-height: 100vh;
        display: flex;
        justify-content: center;
        align-items: center;
        background-color: #f0f2f5;
      }

      /* 加载动画容器 */
      .loading-spinner {
        /* 动画大小 */
        width: 50px;
        height: 50px;

        /* 创建圆形边框 */
        border: 5px solid rgba(255, 255, 255, 0.3); /* 浅色边框 */
        border-top-color: #1677ff; /* 高亮边框(旋转时形成流动效果) */
        border-radius: 50%; /* 圆形 */

        /* 旋转动画 */
        animation: spin 1s ease-in-out infinite;
      }

      /* 旋转动画定义 */
      @keyframes spin {
        to {
          /* 360度旋转 */
          transform: rotate(360deg);
        }
      }
    </style>
  </head>
  <body>
    <!-- 加载动画元素 -->
    <div class="loading-spinner"></div>
  </body>
</html>

1133. ts 有哪些常用的关键词【热度: 178】【TypeScript】【出题公司: 美团】

关键词:ts 关键词

作者备注

这个问题主要是对 ts 类型熟悉程度的考察, 比直接问 number、string 等基础类型有意义。

TypeScript 在 JavaScript 基础上扩展了许多用于类型定义和类型控制的关键字,这些关键字是构建 TypeScript 类型系统的核心。以下是常用的关键词分类及说明:

以下是 TypeScript 常用关键字的分类汇总表:

| 分类 | 关键字/操作符 | 主要用途 | | ------------- | ------------------------------ | ------------------------------------------------------------ | ---------------------------- | | 基础类型 | number string boolean | 定义数字、字符串、布尔值类型 | | | null undefined | 定义空值类型 | | | void | 表示函数无返回值 | | | any unknown | any 关闭类型检查;unknown 安全的未知类型(需断言后使用) | | | never | 表示永远不会发生的类型(如抛出错误) | | 复合类型 | arrayT[]Array<T>) | 定义数组类型 | | | interface | 定义对象结构(可扩展、继承) | | | type | 类型别名(支持联合、交叉等复杂类型) | | | enum | 定义命名常量集合 | | | tuple[T1, T2]) | 固定长度和类型的数组 | | 类型操作 | |(联合类型) | 表示"或"关系(类型可选其一) | | | &(交叉类型) | 表示"且"关系(合并多个类型) | | | extends | 泛型约束;接口/类继承 | | | infer | 条件类型中推断类型(如提取函数返回值) | | | keyof | 获取对象类型的所有键名组成的联合类型 | | | typeof | 获取变量/属性的类型 | | | as | 类型断言(指定变量的实际类型) | | 函数/泛型 | function | 定义函数(指定参数和返回值类型) | | | 泛型(T U 等) | 定义通用类型,实现类型复用 | | 模块/命名 | export import | 模块导出/导入 | | | namespace | 定义命名空间(避免全局变量冲突) | | 其他常用 | readonly | 定义只读属性或数组 | | | declare | 声明全局变量/模块类型(用于 .d.ts 文件) | | | abstract | 定义抽象类/抽象方法(需子类实现) |

这个表涵盖了日常开发中最常用的关键字,可根据场景快速查阅。核心重点关注 类型声明number/interface 等)、类型操作extends/keyof/infer)和 泛型相关 关键字,它们是 TypeScript 类型系统的核心。

1134. 对比一下 ts 和 jsdoc【热度: 126】【TypeScript】

关键词:ts 对比 jsdoc

TypeScript(TS)和 JSDoc 都是用于增强 JavaScript 代码类型安全性的工具,但它们的实现方式和适用场景有显著差异。以下是两者的对比分析及优劣总结:

TypeScript 与 JSDoc 对比表

维度 TypeScript(TS) JSDoc
本质 是 JavaScript 的超集,需要编译为 JS 才能运行,自带完整的类型系统 是 JavaScript 的注释规范,通过注释标注类型,无需编译,浏览器/Node 可直接运行
类型定义方式 使用专门的类型语法(如 : numberinterfacetype 等) 使用注释标签(如 @param@returns@type 等)标注类型
类型检查时机 编译时强制类型检查,错误会在开发阶段暴露 依赖 IDE/编辑器(如 VS Code)的 TypeScript 服务进行类型检查,非强制
功能丰富度 类型系统强大(泛型、交叉/联合类型、条件类型、枚举等) 支持基础类型标注,复杂类型(如泛型约束、条件类型)表达能力有限
生态与工具 生态成熟,支持所有主流框架(React、Vue 等),有大量类型声明文件(.d.ts 依赖 TypeScript 语言服务,可复用 .d.ts 文件,但工具链集成较弱
学习成本 较高,需学习专门的类型语法和概念 较低,基于注释,语法简单,熟悉 JS 即可快速上手
项目侵入性 高,需将文件改为 .ts 后缀,可能需要调整构建流程 低,不改变 JS 代码结构,仅添加注释,原有 JS 项目可平滑接入
运行时影响 无(编译后为纯 JS),但类型信息会被完全擦除 无(注释不影响运行),类型信息仅存在于代码中
适用场景 大型项目、团队协作、对类型安全性要求高的场景 小型项目、快速原型、希望保持纯 JS 但需要基础类型提示的场景

核心优劣总结

TypeScript 的优势:
  1. 强类型检查:编译阶段强制校验,能提前发现更多类型错误,减少运行时问题。
  2. 丰富的类型功能:支持泛型、条件类型、枚举等高级特性,能精确描述复杂数据结构。
  3. 工具链完善:与主流 IDE、构建工具(Webpack、Vite)深度集成,开发体验好。
  4. 团队协作友好:类型定义作为"活文档",清晰传达接口设计,降低沟通成本。
TypeScript 的劣势:
  1. 学习成本高 :需要掌握额外的类型语法和概念(如 inferkeyof 等)。
  2. 项目侵入性强:需要修改文件后缀、配置编译流程,对纯 JS 项目迁移有一定成本。
  3. 编译开销:增加编译步骤,可能略微影响开发效率(尤其大型项目)。
JSDoc 的优势:
  1. 零成本接入:无需修改代码结构,仅通过注释标注类型,原有 JS 项目可直接使用。
  2. 无编译步骤:保留纯 JS 特性,浏览器/Node 可直接运行,适合快速迭代。
  3. 学习成本低:语法简单,基于注释标签,熟悉 JS 的开发者可快速上手。
JSDoc 的劣势:
  1. 类型能力有限:复杂类型(如泛型约束、条件类型)难以表达,类型检查较弱。
  2. 非强制检查:依赖 IDE 提示,无法在构建阶段强制报错,可能遗漏类型问题。
  3. 注释冗余:大量类型注释会增加代码体积,影响可读性(尤其复杂类型)。

总结建议

  • 若项目规模大、团队协作频繁、对类型安全要求高,优先选 TypeScript
  • 若项目小、追求快速开发、希望保持纯 JS 生态,可选 JSDoc 作为轻量方案。
  • 实际开发中,两者也可结合使用(如用 JSDoc 在 TS 项目中补充复杂类型注释)。

1136. react 开发的应用里面, 如何给系统设置一个全局的崩溃的提示页面【热度: 725】【web 框架】【出题公司: 小米】

关键词:崩溃提示

在 React 应用中设置全局崩溃提示页面,核心是通过 错误边界边界(Error Boundary) 来捕获子组件树中的 JavaScript 错误,并显示备用 UI。以下是具体实现方案:

实现说明

  1. 错误边界组件(ErrorBoundary)

    • 这是一个 React 类组件,利用 React 的错误捕获生命周期 getDerivedStateFromErrorcomponentDidCatch 捕获子组件错误
    • getDerivedStateFromError:用于更新状态,触发错误 UI 渲染
    • componentDidCatch:用于记录错误信息(可上报到服务端)
    • 提供重试和返回首页功能,提升用户体验
jsx 复制代码
import React from "react";
import { Button } from "your-ui-library"; // 可替换为你的 UI 库或自定义按钮

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      hasError: false,
      error: null,
      errorInfo: null,
    };
  }

  // 捕获子组件树中的错误
  static getDerivedStateFromError(error) {
    // 更新 state,下一次渲染将显示错误 UI
    return { hasError: true };
  }

  // 记录错误信息(可用于上报)
  componentDidCatch(error, errorInfo) {
    this.setState({
      error: error,
      errorInfo: errorInfo,
    });

    // 可选:将错误信息上报到服务端
    console.error("全局错误捕获:", error, errorInfo);
    // 实际项目中可以调用接口上报:
    // fetch('/api/log-error', {
    //   method: 'POST',
    //   body: JSON.stringify({ error: error.message, stack: errorInfo.componentStack })
    // });
  }

  // 重置错误状态(重新加载应用)
  resetErrorHandler = () => {
    this.setState({
      hasError: false,
      error: null,
      errorInfo: null,
    });
    // 可选:如果需要完全重置应用状态,可以刷新页面
    // window.location.reload();
  };

  render() {
    if (this.state.hasError) {
      // 错误发生时显示的崩溃页面
      return (
        <div className="global-error-container">
          <div className="error-content">
            <h2>😱 应用发生错误</h2>
            <p>很抱歉,页面出现了意外错误,请尝试刷新或联系管理员。</p>

            {/* 可选:显示错误详情(生产环境可隐藏) */}
            {process.env.NODE_ENV === "development" && (
              <details style={{ whiteSpace: "pre-wrap" }}>
                <summary>错误详情</summary>
                {this.state.error?.message}
                <br />
                {this.state.errorInfo?.componentStack}
              </details>
            )}

            <div className="error-actions">
              <Button onClick={this.resetErrorHandler} variant="primary">
                重试
              </Button>
              <Button onClick={() => (window.location.href = "/")} variant="secondary" style={{ marginLeft: "10px" }}>
                返回首页
              </Button>
            </div>
          </div>
        </div>
      );
    }

    // 如果没有错误,渲染子组件
    return this.props.children;
  }
}

export default ErrorBoundary;
  1. 全局应用

    • 在应用入口(App.jsx)用 ErrorBoundary 包裹整个应用,确保所有子组件的错误都能被捕获
    • 错误边界会自动捕获其内部所有组件(包括嵌套组件)的渲染错误、生命周期错误等
jsx 复制代码
import React from "react";
import ErrorBoundary from "./ErrorBoundary";
import Router from "./Router"; // 你的路由组件
import GlobalStyle from "./GlobalStyle"; // 全局样式

function App() {
  return (
    // 用错误边界包裹整个应用
    <ErrorBoundary>
      <GlobalStyle />
      <Router />
    </ErrorBoundary>
  );
}

export default App;

注意事项

  • 错误边界不能捕获以下错误

    • 事件处理函数中的错误(需手动 try/catch)
    • 异步代码中的错误(如 setTimeout、Promise)
    • 错误边界自身的错误
    • 服务端渲染的错误
  • 对于异步错误(如 API 请求失败),需要额外在代码中处理(如 try/catch 或状态管理)

  • 可以根据需要扩展错误边界,例如:

    • 增加错误分类显示不同提示
    • 实现自动重试逻辑
    • 集成错误监控工具(如 Sentry)

通过这种方式,你的 React 应用就能拥有一个全局的崩溃处理机制,在发生错误时给用户友好的提示,而不是白屏或控制台报错。

高级开发者相关问题【共计 6 道题】

1122. 将网页 dom 元素转为图片, 有哪些办法【热度: 41】【web 应用场景】

关键词:dom 转图片

在前端开发中,将 DOM 元素转换为图片有以下几种常见的方法:

1. 使用 HTML5 Canvas API (推荐)

这是最常用的方法,通过 Canvas 的drawImagegetContext方法绘制 DOM 内容,然后导出为图片。这种方法需要先将 DOM 内容转换为 Canvas 可绘制的格式,通常使用html2canvas库简化这个过程。

实现步骤

  1. 安装 html2canvas
bash 复制代码
npm install html2canvas
  1. 示例代码
javascript 复制代码
import html2canvas from "html2canvas";

// 点击按钮触发截图
document.getElementById("captureBtn").addEventListener("click", async () => {
  const element = document.getElementById("targetElement");

  try {
    // 将DOM元素转换为Canvas
    const canvas = await html2canvas(element);

    // 将Canvas转换为图片URL
    const imgData = canvas.toDataURL("image/png");

    // 创建下载链接
    const link = document.createElement("a");
    link.download = "screenshot.png";
    link.href = imgData;
    link.click();
  } catch (error) {
    console.error("截图失败:", error);
  }
});

优点 :兼容性好,支持大多数现代浏览器。
缺点:复杂元素(如阴影、SVG、iframe)可能渲染不完整。

2. 使用 Canvas 直接绘制

如果你只需要绘制简单的文本或图形,可以直接使用 Canvas API 手动绘制:

javascript 复制代码
document.getElementById("captureBtn").addEventListener("click", () => {
  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");

  // 设置Canvas尺寸
  canvas.width = 300;
  canvas.height = 200;

  // 手动绘制内容
  ctx.fillStyle = "white";
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  ctx.fillStyle = "black";
  ctx.font = "20px Arial";
  ctx.fillText("Hello, World!", 100, 100);

  // 导出为图片
  const imgData = canvas.toDataURL("image/png");
  const link = document.createElement("a");
  link.download = "manual-drawing.png";
  link.href = imgData;
  link.click();
});

优点 :无需依赖外部库,可控性强。
缺点:仅适用于简单场景,复杂 DOM 难以手动绘制。

3. 使用 SVG

将 DOM 转换为 SVG 格式,然后导出为图片:

javascript 复制代码
document.getElementById("captureBtn").addEventListener("click", () => {
  const targetElement = document.getElementById("targetElement");

  // 创建SVG元素
  const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  svg.setAttribute("width", targetElement.offsetWidth);
  svg.setAttribute("height", targetElement.offsetHeight);

  // 创建foreignObject嵌入HTML
  const foreignObject = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject");
  foreignObject.setAttribute("width", "100%");
  foreignObject.setAttribute("height", "100%");

  // 克隆目标元素并添加到foreignObject
  const clonedElement = targetElement.cloneNode(true);
  foreignObject.appendChild(clonedElement);
  svg.appendChild(foreignObject);

  // 转换为DataURL
  const svgData = new XMLSerializer().serializeToString(svg);
  const imgData = "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(svgData)));

  // 下载图片
  const link = document.createElement("a");
  link.download = "svg-export.png";
  link.href = imgData;
  link.click();
});

优点 :矢量图形,可无限缩放不失真。
缺点:对复杂 CSS 和 JavaScript 交互支持有限。

4. 使用第三方 API

对于服务器端渲染或复杂场景,可以使用第三方 API 如:

  • Puppeteer (Node.js 库):通过无头 Chrome 浏览器渲染页面并截图。
  • html2pdf.js:将 HTML 转换为 PDF 或图片。
  • ImgKit:基于 WebKit 的服务器端渲染服务。

示例(Puppeteer)

javascript 复制代码
const puppeteer = require("puppeteer");

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  await page.goto("https://example.com");
  await page.screenshot({ path: "page.png" });

  await browser.close();
})();

优点 :渲染效果最接近浏览器,支持复杂场景。
缺点:需要服务器支持,增加了部署复杂度。

选择建议

  • 简单静态内容:使用 Canvas 直接绘制。
  • 复杂 DOM 元素 :使用html2canvas库。
  • 需要高质量渲染:使用 Puppeteer 等服务器端方案。
  • 需要矢量图形:使用 SVG 方法。

根据你的具体需求选择最合适的方法即可。

1125. 介绍一下 git diff【热度: 396】【web 应用场景】

关键词:git diff

作者备注

这个比较冷门, 平常很多时候都用不上, 基本上可以当做科普了解 如果当面试官问到:代码有问题, 怎么排查是哪一个 commit 引入的, 可以参考以下的回答

git diff 是 Git 中用于查看文件修改差异的核心命令,能够展示不同版本、不同状态之间的代码变更,帮助你跟踪和理解代码的变化过程。

核心作用

比较不同版本或不同状态下的文件内容差异,主要场景包括:

  • 工作区与暂存区的差异
  • 暂存区与最新提交的差异
  • 不同提交之间的差异
  • 不同分支之间的差异

常用用法

1. 查看工作区与暂存区的差异(最常用)
bash 复制代码
git diff
  • 显示工作区中已修改但未暂存 (未执行 git add)的文件与暂存区的差异
  • 输出格式:- 表示删除的内容,+ 表示新增的内容,行号用于定位位置
2. 查看暂存区与最新提交的差异
bash 复制代码
git diff --cached  # 或 git diff --staged
  • 显示已暂存(执行过 git add)但未提交 的内容与最近一次提交(HEAD)的差异
  • 常用于提交前确认暂存的内容是否正确
3. 查看工作区与最新提交的差异
bash 复制代码
git diff HEAD
  • 同时显示未暂存已暂存的所有修改与最新提交的差异
  • 相当于 git diff(工作区 vs 暂存区) + git diff --cached(暂存区 vs HEAD)的合并结果
4. 比较两个提交之间的差异
bash 复制代码
git diff <提交ID1> <提交ID2>
  • 示例:比较 a1b2c3de4f5g6h 两个提交的差异

    bash 复制代码
    git diff a1b2c3d e4f5g6h
  • 若只关心某个文件的差异,可在最后指定文件名:

    bash 复制代码
    git diff a1b2c3d e4f5g6h src/index.js
5. 比较两个分支之间的差异
bash 复制代码
git diff <分支1> <分支2>
  • 示例:比较 feature/login 分支和 main 分支的差异

    bash 复制代码
    git diff feature/login main
6. 查看某次提交相对于上一次提交的差异
bash 复制代码
git diff <提交ID>^ <提交ID>  # ^ 表示该提交的父提交
# 简化写法:
git diff <提交ID>~1 <提交ID>
  • 更简洁的方式:直接查看某次提交的修改内容

    bash 复制代码
    git show <提交ID>  # 相当于 git diff <提交ID>^ <提交ID>

输出格式说明

git diff 的输出通常包含以下部分:

diff 复制代码
diff --git a/src/index.js b/src/index.js  # 比较的文件
index 1234567..abcdefg 100644            # 文件的索引信息
--- a/src/index.js                        # 源文件(旧版本)
+++ b/src/index.js                        # 目标文件(新版本)
@@ -5,7 +5,7 @@ function greet() {                # 差异所在的行范围
   console.log("Hello, world!");
-  console.log("This is old code");
+  console.log("This is new code");       # +表示新增内容
   return true;
 }

实用选项

  • -w:忽略空白字符的差异(如空格、换行的调整)

    bash 复制代码
    git diff -w  # 忽略空白差异
  • --stat:只显示文件修改的统计信息(不显示具体内容)

    bash 复制代码
    git diff --stat  # 例如:src/index.js | 2 +-(表示该文件有2行修改,1行新增1行删除)
  • -p:显示完整的差异内容(默认就是这个行为,可省略)

总结

git diff 是代码审查和变更跟踪的重要工具,核心是通过比较不同"版本快照"之间的差异,帮助你:

  • 提交前确认修改内容
  • 回顾历史变更
  • 了解分支之间的差异
  • 排查代码问题

熟练使用 git diff 能大幅提升对代码变更的掌控力,是日常 Git 操作中不可或缺的命令。

1127. less 是否支持条件判定【热度: 112】【web 应用场景】【出题公司: 腾讯】

关键词:less 条件判定

是的,Less 完全支持条件判定,其核心通过 when 关键字 实现,同时可结合比较运算符、逻辑运算符构建复杂的条件逻辑,主要用于动态控制样式规则的生效与否(如根据变量值切换样式、适配不同场景)。

一、核心语法:when 条件判断

Less 的条件判定并非像 JavaScript 那样的 if-else 语句,而是以 "条件附加在选择器/混合(Mixin)后" 的形式存在,只有当条件满足时,对应的样式才会被编译。

1. 基础语法结构
less 复制代码
// 格式:选择器 / 混合名 when (条件) { 样式 }
选择器 when (条件) {
  // 条件满足时生效的样式
}

// 示例:当 @width 大于 500px 时,设置容器宽度
.container when (@width > 500px) {
  width: @width;
  padding: 20px;
}

二、支持的条件类型

Less 允许在 when 中使用 比较运算符逻辑运算符类型检查函数,覆盖绝大多数场景需求。

1. 比较运算符

用于数值(如长度、数字、百分比)的比较,支持 6 种运算符:

  • >:大于
  • <:小于
  • >=:大于等于
  • <=:小于等于
  • ==:等于(值和单位需完全匹配,如 500px == 500 不成立)
  • !=:不等于

示例:根据屏幕宽度变量适配样式

less 复制代码
@screen-width: 1200px;

// 大屏幕(>1024px)
.header when (@screen-width > 1024px) {
  font-size: 18px;
  padding: 0 40px;
}

// 中屏幕(768px ~ 1024px)
.header when (@screen-width >= 768px) and (@screen-width <= 1024px) {
  font-size: 16px;
  padding: 0 20px;
}

// 小屏幕(<768px)
.header when (@screen-width < 768px) {
  font-size: 14px;
  padding: 0 10px;
}

编译后(因 @screen-width=1200px),仅大屏幕样式生效:

css 复制代码
.header {
  font-size: 18px;
  padding: 0 40px;
}
2. 逻辑运算符

用于组合多个条件,支持 3 种逻辑关系:

  • and:逻辑"与"(所有条件均满足才生效)
  • ,(逗号):逻辑"或"(任意一个条件满足即生效,注意不是 or
  • not:逻辑"非"(否定单个条件,需用括号包裹)

示例:逻辑组合的应用

less 复制代码
@theme: "dark";
@font-size: 16; // 无单位(后续需拼接)

// 条件1:主题为 dark OR 字体大小 >= 16
.text-style when (@theme == "dark"), (@font-size >= 16) {
  color: #fff;
  background: #333;
}

// 条件2:主题不是 light AND 字体大小 < 20
.text-style when not (@theme == "light") and (@font-size < 20) {
  font-weight: 500;
}

编译后(@theme=dark@font-size=16 满足所有条件):

css 复制代码
.text-style {
  color: #fff;
  background: #333;
  font-weight: 500;
}
3. 类型检查函数

用于判断变量的 类型是否为数值,常见函数如下:

函数 作用 示例
isnumber(@value) 判断是否为数字(无论是否有单位) isnumber(123)true
isstring(@value) 判断是否为字符串 isstring("red")true
iscolor(@value) 判断是否为颜色值(如 #fffred iscolor(#333)true
isurl(@value) 判断是否为 URL(如 url(xxx.png) isurl(url(img.jpg))true
isunit(@value, 单位) 判断是否为指定单位的数值 isunit(50px, px)true

示例:类型检查控制样式

less 复制代码
@border-width: 2px;
@border-color: "#000"; // 字符串类型的颜色

// 条件:边框宽度是 px 单位,且边框颜色是字符串(需转换为颜色)
.border when (isunit(@border-width, px)) and (isstring(@border-color)) {
  border: @border-width solid @border-color; // Less 会自动将字符串颜色转为颜色值
}

编译后:

css 复制代码
.border {
  border: 2px solid #000;
}

三、进阶用法:结合混合(Mixin)

条件判定在 混合(Mixin) 中使用最广泛,可实现"动态复用样式",甚至模拟"if-else 分支"。

1. 带条件的混合

定义仅在特定条件下生效的混合,调用时自动判断是否执行:

less 复制代码
// 定义混合:仅当 @radius 是数字时,设置圆角
.border-radius(@radius) when (isnumber(@radius)) {
  border-radius: @radius * 1px; // 统一转为 px 单位
}

// 调用混合
.card {
  .border-radius(8); // 满足条件(8 是数字),编译为 border-radius: 8px
}

.button {
  .border-radius("8"); // 不满足条件("8" 是字符串),无样式输出
}
2. 模拟"if-else"分支

通过多个 when 条件的"互斥性",实现类似 if-else 的逻辑(即"满足 A 则执行 A,否则执行 B"):

less 复制代码
@is-disabled: true;

// 条件1:如果禁用(if)
.button-style when (@is-disabled = true) {
  background: #ccc;
  cursor: not-allowed;
  color: #999;
}

// 条件2:如果未禁用(else)
.button-style when (@is-disabled = false) {
  background: #007bff;
  cursor: pointer;
  color: #fff;
}

// 调用
.disabled-btn {
  .button-style; // 因 @is-disabled=true,编译为禁用样式
}

四、注意事项

  1. 条件仅支持"编译时判定" :Less 是预编译语言,条件判断基于 编译时的变量值 ,无法动态响应运行时(如浏览器窗口大小变化),运行时适配需结合 CSS @media 查询。

  2. 键名与变量的区别 :条件中使用变量时,需确保变量已定义;若误写为未定义的键名(如 when (screen-width > 1000px)),Less 会视为 undefined,条件判定为 false

  3. 与 CSS @media 的分工

    • Less 条件:用于 编译时的静态变量控制(如主题切换、固定参数适配);
    • CSS @media:用于 运行时的动态环境适配 (如屏幕宽度、设备像素比)。 两者可结合使用(如 Less 变量动态生成 @media 条件):
    less 复制代码
    @breakpoint: 768px;
    @media (max-width: @breakpoint) {
      .container when (@columns = 2) {
        // Less 条件 + CSS media
        display: grid;
        grid-template-columns: repeat(2, 1fr);
      }
    }

总结

Less 的条件判定通过 when 关键字实现,支持比较、逻辑、类型检查,核心价值是 在编译时动态控制样式的生成 ,尤其适合与混合结合实现可复用的条件样式。日常开发中,需根据"是否需要编译时变量控制"选择 Less 条件(静态)或 CSS @media(动态),两者配合可覆盖绝大多数适配场景。

1128. less 有那些高级特性, 驱使你们项目工程去使用它【热度: 336】【web 应用场景】【出题公司: 腾讯】

关键词:less 特性

Less 作为一款流行的 CSS 预处理器,核心价值在于通过增强 CSS 的可编程性、复用性和可维护性,简化样式开发流程。除了基础的变量、嵌套语法,它还提供了诸多"高级特性",这些特性能应对复杂场景(如组件样式封装、主题切换、动态样式计算等)。以下是 Less 核心高级特性的详细解析,结合使用场景和示例帮助理解:

一、条件判定(Guards)

Less 不支持传统编程语言的 if-else 语句,但通过 Guards(守卫) 实现了"基于条件匹配样式规则"的能力,分为「规则守卫」和「混合守卫」,核心是通过表达式判断是否应用样式。

1. 规则守卫(Guards on Rulesets)

给选择器添加条件,只有满足条件时,该选择器下的样式才会生效。
语法& when (条件表达式)& 代表当前选择器)
支持的运算符>, <, >=, <=, ==, !=,以及逻辑运算符 and, or, not

示例:根据屏幕宽度动态调整字体大小

less 复制代码
// 定义变量存储断点
@sm: 768px;
@md: 1024px;

.container {
  font-size: 14px; // 默认样式

  // 屏幕 >= 768px 时生效
  & when (@media-width >= @sm) {
    font-size: 16px;
  }

  // 屏幕 >= 1024px 时生效(and 连接多条件)
  & when (@media-width >= @md) and (@theme = "dark") {
    font-size: 18px;
    color: #fff;
  }
}
2. 混合守卫(Guards on Mixins)

给混合(Mixin)添加条件,只有满足条件时,混合中的样式才会被注入。常用于"动态复用样式片段"。

示例:根据主题切换按钮样式

less 复制代码
// 定义带条件的混合
.button-style(@theme) when (@theme = "primary") {
  background: #1890ff;
  border: 1px solid #1890ff;
}

.button-style(@theme) when (@theme = "danger") {
  background: #ff4d4f;
  border: 1px solid #ff4d4f;
}

// 使用混合(传入不同主题,触发不同条件)
.btn-primary {
  .button-style("primary");
  color: #fff;
}

.btn-danger {
  .button-style("danger");
  color: #fff;
}

二、高级变量特性

Less 的变量不仅支持"值存储",还支持变量插值变量作用域变量运算,灵活应对动态样式场景。

1. 变量插值(Variable Interpolation)

将变量值插入到选择器名、属性名、URL、字符串 中,实现"动态生成标识符"。
语法@{变量名}

示例:动态生成选择器和 URL

less 复制代码
// 1. 动态选择器(如组件前缀)
@component-prefix: "my-btn";

.@{component-prefix} {
  // 最终编译为 .my-btn
  padding: 8px 16px;
}

.@{component-prefix}-disabled {
  // 最终编译为 .my-btn-disabled
  opacity: 0.6;
  cursor: not-allowed;
}

// 2. 动态 URL(如图片路径)
@img-path: "../assets/img";

.logo {
  background: url("@{img-path}/logo.png"); // 最终编译为 url("../assets/img/logo.png")
}

// 3. 动态属性名(如主题色属性)
@property: "color";
@theme-color: #1890ff;

.title {
  @{property}: @theme-color; // 最终编译为 color: #1890ff
}
2. 变量作用域(Variable Scope)

Less 变量遵循"就近原则":局部作用域(如选择器、混合内部)的变量会覆盖全局作用域的变量,且支持"向上查找"(局部没有时,查找父级作用域)。

示例:作用域优先级

less 复制代码
@color: red; // 全局变量

.container {
  @color: blue; // 局部变量(覆盖全局)
  .box {
    color: @color; // 优先使用局部变量,最终为 blue
  }
}

.text {
  color: @color; // 无局部变量,使用全局变量,最终为 red
}
3. 变量运算(Operations)

支持对数字、颜色、长度单位 进行算术运算(+, -, *, /),自动处理单位兼容(如 pxrem 混合运算)。

示例:动态计算样式值

less 复制代码
@base-padding: 10px;
@base-font-size: 14px;

.card {
  // 数字运算(padding = 基础值 * 1.5)
  padding: @base-padding * 1.5; // 最终 15px

  // 颜色运算(深色 = 基础色降低亮度)
  @base-color: #1890ff;
  background: @base-color - #333; // 最终 #0066cc

  // 单位混合运算(font-size = 基础值 + 2rem)
  font-size: @base-font-size + 2rem; // 最终 16px(Less 自动统一单位)
}

三、混合(Mixins)进阶

混合是 Less 的核心复用特性,除了基础的"样式片段复用",还支持带参数混合、默认参数、剩余参数,甚至可以"返回值"(通过变量传递)。

1. 带参数混合(Parametric Mixins)

给混合定义参数,使用时传入不同值,实现"个性化复用"。

示例:通用圆角混合

less 复制代码
// 定义带参数的混合(@radius 为参数)
.border-radius(@radius) {
  -webkit-border-radius: @radius;
  -moz-border-radius: @radius;
  border-radius: @radius;
}

// 使用混合(传入不同半径值)
.btn {
  .border-radius(4px); // 小圆角
}

.card {
  .border-radius(8px); // 大圆角
}
2. 默认参数(Default Values)

给混合参数设置默认值,使用时可省略该参数(自动使用默认值)。

示例:带默认值的阴影混合

less 复制代码
// 定义混合(@color 默认 #000,@opacity 默认 0.2)
.box-shadow(@x: 0, @y: 0, @blur: 4px, @color: #000, @opacity: 0.2) {
  box-shadow: @x @y @blur rgba(@color, @opacity);
}

// 使用混合(省略部分参数,使用默认值)
.card {
  .box-shadow(0, 2px); // 省略 @blur(默认 4px)、@color(默认 #000)、@opacity(默认 0.2)
  // 最终编译为:box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2)
}
3. 剩余参数(Variadic Arguments)

当混合参数数量不确定时,用 ... 接收"剩余所有参数",类似 JavaScript 的 rest 参数。

示例:灵活的过渡动画混合

less 复制代码
// 定义混合(@props 接收所有过渡属性,@duration 默认 0.3s)
.transition(@props..., @duration: 0.3s) {
  transition: @props @duration ease;
}

// 使用混合(传入多个过渡属性)
.btn {
  .transition(color, background); // @props 接收 [color, background]
  // 最终编译为:transition: color background 0.3s ease
}

.card {
  .transition(transform, opacity, 0.5s); // 自定义 duration 为 0.5s
  // 最终编译为:transition: transform opacity 0.5s ease
}

四、导入(Import)进阶

Less 的 @import 不仅能导入其他 Less 文件,还支持条件导入引用导入导入变量/混合,灵活管理样式模块。

1. 条件导入(Conditional Import)

结合 Guards 实现"满足条件时才导入文件",常用于"按需加载主题/适配样式"。

示例:根据主题导入不同样式文件

less 复制代码
@theme: "dark"; // 可动态切换为 "light"

// 条件:主题为 dark 时,导入深色主题文件
@import (multiple) "theme-dark.less" when (@theme = "dark");

// 条件:主题为 light 时,导入浅色主题文件
@import (multiple) "theme-light.less" when (@theme = "light");
  • 注:(multiple) 表示"允许重复导入同一文件"(默认不允许)。
2. 引用导入(Reference Import)

@import (reference) 导入文件时,仅引用文件中的混合、变量,不编译文件本身的样式,避免冗余代码。

示例:引用工具类文件(仅用混合,不编译工具类样式)

less 复制代码
// 导入工具类文件(reference 表示仅引用,不编译 utils.less 中的选择器)
@import (reference) "utils.less";

// 使用 utils.less 中的混合(如 .clearfix)
.container {
  .clearfix(); // 仅注入 .clearfix 的样式,utils.less 其他样式不编译
}
3. 导入变量/混合(Import for Variables/Mixins)

导入文件时,可直接使用目标文件中的变量和混合,实现"样式模块拆分"(如将变量、混合、组件样式分别放在不同文件)。

示例:模块化拆分样式

less 复制代码
// 1. variables.less(存储全局变量)
@primary-color: #1890ff;
@font-size-base: 14px;

// 2. mixins.less(存储通用混合)
.clearfix() {
  &::after {
    content: "";
    display: table;
    clear: both;
  }
}

// 3. main.less(导入并使用)
@import "variables.less";
@import "mixins.less";

.btn {
  color: @primary-color; // 使用 variables.less 的变量
  font-size: @font-size-base;
}

.container {
  .clearfix(); // 使用 mixins.less 的混合
}

五、循环(Loops)

Less 没有专门的 for/while 循环语法,但通过混合自调用(混合内部调用自身)实现循环效果,常用于"生成重复样式"(如网格系统、层级样式)。

示例 1:生成 1-5 级标题样式

less 复制代码
// 定义循环混合(@n 为当前层级,@max 为最大层级)
.generate-heading(@n, @max) when (@n <= @max) {
  // 动态生成选择器(h1, h2, ..., h@max)
  h@{n} {
    font-size: 16px + (@n - 1) * 4px; // 每级标题增大 4px
    margin-bottom: 8px + (@n - 1) * 2px;
  }
  // 自调用(层级 +1)
  .generate-heading(@n + 1, @max);
}

// 触发循环(生成 h1-h5 样式)
.generate-heading(1, 5);

编译结果

css 复制代码
h1 {
  font-size: 16px;
  margin-bottom: 8px;
}
h2 {
  font-size: 20px;
  margin-bottom: 10px;
}
h3 {
  font-size: 24px;
  margin-bottom: 12px;
}
h4 {
  font-size: 28px;
  margin-bottom: 14px;
}
h5 {
  font-size: 32px;
  margin-bottom: 16px;
}

示例 2:生成网格系统(col-1 到 col-12)

less 复制代码
.generate-col(@n) when (@n <= 12) {
  .col-@{n} {
    width: (@n / 12) * 100%; // 每列宽度 = (n/12)*100%
    float: left;
  }
  .generate-col(@n + 1);
}

.generate-col(1); // 生成 col-1 到 col-12

六、内置函数(Built-in Functions)

Less 提供了丰富的内置函数,覆盖颜色处理、字符串操作、数值计算等场景,无需手动编写复杂逻辑。以下是常用内置函数分类:

函数类别 常用函数 功能说明 示例
颜色处理 darken(@color, @percent) 降低颜色亮度(百分比) darken(#1890ff, 10%) → #096dd9
lighten(@color, @percent) 提高颜色亮度(百分比) lighten(#1890ff, 10%) → #3ba0ff
rgba(@color, @alpha) 设置颜色透明度 rgba(#1890ff, 0.5) → rgba(24,144,255,0.5)
字符串操作 replace(@str, @find, @replace) 替换字符串内容 replace("hello", "h", "H") → "Hello"
upper(@str) 字符串转大写 upper("hello") → "HELLO"
数值计算 ceil(@num) 向上取整 ceil(2.3) → 3
floor(@num) 向下取整 floor(2.7) → 2
percentage(@num) 小数转百分比 percentage(0.25) → 25%
其他 typeof(@value) 判断值的类型(number/string/color 等) typeof(#fff) → "color"

示例:用内置函数处理主题色

less 复制代码
@primary: #1890ff;

.btn {
  background: @primary;
  //  hover 时加深 10% 亮度
  &:hover {
    background: darken(@primary, 10%);
  }
  //  active 时降低透明度到 0.8
  &:active {
    background: rgba(@primary, 0.8);
  }
}

总结

Less 的高级特性围绕"复用、动态、可维护"三大核心设计,适合复杂项目的样式开发:

  • 动态切换样式(如主题、响应式):用「条件判定」「变量插值」「条件导入」;
  • 复用样式片段(如组件、工具类):用「带参数混合」「剩余参数」「引用导入」;
  • 生成重复样式(如网格、层级):用「循环」;
  • 处理颜色/数值:用「内置函数」「变量运算」。

合理搭配这些特性,能大幅减少冗余 CSS 代码,提升样式开发效率和可维护性。

1132. ts 里面 infer 是什么关键词, 怎么用【热度: 975】【TypeScript】【出题公司: 美团】

关键词:ts infer

在 TypeScript 中,infer 是一个用于类型推断 的关键字,通常与条件类型(Conditional types)配合使用,用于从泛型类型中提取或推断出某个具体类型。它的核心作用是"让 TypeScript 自动推导出我们需要的类型",而无需需手动指定。

基本语法与作用

infer 只能在条件类型的 extends 子句中使用,语法格式如下:

typescript 复制代码
type 类型名<T> = T extends 某个类型<infer 待推断类型> ? 待推断类型 : 其他类型;
  • infer X 表示"声明一个需要推断的类型变量 X"
  • TypeScript 会自动分析 T 的结构,推导出 X 的具体类型

典型使用场景

1. 提取函数的返回值类型

最常见的场景之一:从函数类型中提取其返回值类型。

typescript 复制代码
// 定义一个条件类型,提取函数的返回值类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

// 使用示例
function getUser() {
  return { name: "张三", age: 20 };
}

// 推断 getUser 函数的返回值类型
type User = ReturnType<typeof getUser>;
// User 的类型为 { name: string; age: number }
  • T extends (...args: any[]) => infer R 表示:如果 T 是一个函数,就推断其返回值类型为 R
  • 最终 User 被推断为函数 getUser 的返回值类型
2. 提取函数的参数类型

类似地,可以提取函数的参数类型(单个参数或参数列表)。

typescript 复制代码
// 提取单个参数类型
type ParamType<T> = T extends (param: infer P) => any ? P : never;

// 提取参数列表类型(返回元组)
type ParamsType<T> = T extends (...args: infer P) => any ? P : never;

// 使用示例
function sum(a: number, b: string): boolean {
  return a + Number(b) > 10;
}

type SumParam = ParamType<typeof sum>; // 错误!因为函数有多个参数,这里会返回 never
type SumParams = ParamsType<typeof sum>; // [number, string](参数列表组成的元组)
type SumReturn = ReturnType<typeof sum>; // boolean(返回值类型)
3. 提取数组的元素类型

从数组类型中推断出元素的类型。

typescript 复制代码
// 提取数组元素类型
type ArrayItem<T> = T extends Array<infer Item> ? Item : T;

// 使用示例
type NumberItem = ArrayItem<number[]>; // number
type StringItem = ArrayItem<string[]>; // string
type UserItem = ArrayItem<{ name: string }[]>; // { name: string }
type Primitive = ArrayItem<boolean>; // boolean(非数组类型则返回自身)
4. 提取 Promise 的 resolve 类型

Promise 类型中推断出其最终解析(resolve)的类型。

typescript 复制代码
// 提取 Promise 解析的类型
type PromiseResolve<T> = T extends Promise<infer R> ? R : T;

// 使用示例
type Resolve1 = PromiseResolve<Promise<string>>; // string
type Resolve2 = PromiseResolve<Promise<{ id: number }>>; // { id: number }
type Resolve3 = PromiseResolve<number>; // number(非 Promise 类型则返回自身)
5. 嵌套推断(复杂结构)

infer 支持多层嵌套推断,可用于复杂类型结构的提取。

typescript 复制代码
// 从 { data: T } 结构中提取 T
type ExtractData<T> = T extends { data: infer D } ? D : T;

// 嵌套推断:从 Promise<{ data: T }> 中提取 T
type ExtractPromiseData<T> = T extends Promise<{ data: infer D }> ? D : T;

// 使用示例
type Data1 = ExtractData<{ data: { name: string } }>; // { name: string }
type Data2 = ExtractPromiseData<Promise<{ data: number[] }>>; // number[]

注意事项

  1. 只能在条件类型中使用infer 不能单独使用,必须放在 T extends ... 的子句中。

  2. 推断的不确定性 :如果 TypeScript 无法明确推断类型(如多种可能的匹配),会返回 never 或联合类型。

    typescript 复制代码
    type Ambiguous<T> = T extends (a: infer A, b: infer A) => any ? A : never;
    type Test = Ambiguous<(x: number, y: string) => void>; // number | string(联合类型)
  3. 与内置工具类型的关系 :TypeScript 内置的很多工具类型(如 ReturnTypeParameters)都是基于 infer 实现的,例如:

    typescript 复制代码
    // TypeScript 内置的 Parameters 实现
    type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;

总结

infer 是 TypeScript 类型系统中用于自动推断类型的强大工具,核心价值在于:

  • 从复杂类型(如函数、数组、Promise 等)中"提取"我们需要的具体类型
  • 减少手动编写重复类型的工作量,提升类型定义的灵活性和可维护性

它最常见的应用场景包括提取函数参数/返回值、数组元素、Promise 解析值等,是编写高级类型工具的基础。

1135. TypeScript 中,ReturnType 的作用和用法【TypeScript】

关键词:ts RetrunType

在 TypeScript 中,ReturnType 是一个内置的工具类型 ,用于提取函数的返回值类型。它可以自动推断并返回函数的返回值类型,无需手动手动编写重复的类型定义,是处理函数类型时非常实用的工具。

作用

  • 从给定的函数类型中提取其返回值的类型,避免手动定义与函数返回值相同的类型,减少冗余代码。
  • 当函数返回值类型修改时,通过 ReturnType 提取的类型会自动同步更新,保证类型一致性。

用法

基础语法
typescript 复制代码
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
  • 接收一个泛型参数 T,该参数必须是一个函数类型(通过 extends (...args: any) => any 约束)。
  • 使用 infer R 推断函数的返回值类型 R,最终返回 R
实际示例
  1. 提取普通函数的返回值类型

    typescript 复制代码
    // 定义一个返回对象的函数
    function getUser() {
      return { name: "张三", age: 20, isStudent: false };
    }
    
    // 提取函数返回值类型
    type User = ReturnType<typeof getUser>;
    // User 的类型为:{ name: string; age: number; isStudent: boolean }
  2. 提取箭头函数的返回值类型

    typescript 复制代码
    const calculate = (a: number, b: number) => a + b;
    
    // 提取返回值类型(number)
    type Result = ReturnType<typeof calculate>; // Result = number
  3. 提取泛型函数的返回值类型

    typescript 复制代码
    function createArray<T>(length: number, value: T): T[] {
      return Array(length).fill(value);
    }
    
    // 提取特定调用的返回值类型
    type StringArray = ReturnType<typeof createArray<string>>; // StringArray = string[]
  4. 在类型定义中复用

    typescript 复制代码
    // 定义一个回调函数类型
    type FetchData = (url: string) => Promise<{ code: number; data: string }>;
    
    // 提取该函数返回的 Promise 内部类型
    type FetchResult = ReturnType<FetchData>; // FetchResult = Promise<{ code: number; data: string }>
    
    // 进一步提取 Promise 的 resolve 类型(结合 Awaited)
    type Data = Awaited<FetchResult>; // Data = { code: number; data: string }

注意事项

  1. 仅支持函数类型ReturnType 的参数必须是函数类型,否则会报错。

    typescript 复制代码
    type Invalid = ReturnType<string>; // 报错:string 不是函数类型
  2. typeof 配合使用 :当需要提取具体函数的返回值类型时,需用 typeof 获取该函数的类型(如 typeof getUser)。

  3. 内置工具类型ReturnType 是 TypeScript 内置的,无需手动定义,直接使用即可。

总结

ReturnType 的核心价值是自动同步函数返回值类型,尤其适合以下场景:

  • 函数返回值类型复杂,避免手动重复定义。
  • 函数返回值可能频繁修改,通过 ReturnType 确保依赖其类型的地方自动更新。
  • 在类型层面复用函数返回值结构,提升代码可维护性。
相关推荐
ZXT7 小时前
依赖管理
前端
ZXT7 小时前
vite & webpack
前端
徐小夕7 小时前
开源了一款基于 Vue 3 的高性能多维表格编辑器
前端·vue.js·github
小豆包api7 小时前
Nano-Banana速通指南,爆款 AI 绘图神器低成本接入方案
前端
一枚前端小能手7 小时前
🔥 Vue项目越来越卡?响应式系统的4个性能陷阱
前端·javascript·vue.js
居7然7 小时前
京东科技大模型RAG岗三轮面试全复盘:从八股到开放题的通关指南
人工智能·科技·面试·职场和发展·大模型
纯JS甘特图_MZGantt7 小时前
五分钟打通任督二脉!mzgantt数据导入导出与外部系统无缝集成
javascript
在掘金801107 小时前
Rspack 深度解析:面向 Webpack/Vite 用户
前端
卓伊凡7 小时前
苹果开发中什么是Storyboard?object-c 和swiftui 以及Storyboard到底有什么关系以及逻辑?优雅草卓伊凡
前端·后端