当前位置: 首页 > news >正文

2025面试题——(12)

一、var和let const 的区别

  • 作用域:var 是函数级作用域,let/const 是块级作用域({} 内)。
  • 变量提升:var 存在,let/const 不存在(有暂时性死区)。
  • 重复声明:var 允许,let/const 不允许。
  • 赋值:var/let 可重复赋值,const 声明时必须初始化且不能重新赋值(引用类型属性可改)

二、typeof 返回哪些类型

返回以下 7 种基本类型字符串:
undefinedbooleannumberstringsymbolbigintfunction
以及用于对象(非函数)的 object(注意:null 会返回 object,是历史遗留问题)。
三、列举强制类型转换和隐式类型转换

强制类型转换(显式转换)
  • 转数字:Number()parseInt()parseFloat()
  • 转字符串:String()toString()
  • 转布尔:Boolean()
隐式类型转换(自动转换)
  • 算术运算:1 + "2" → "12"(数字转字符串)
  • 比较运算:"5" == 5 → true(字符串转数字)
  • 逻辑判断:if ("")(空字符串转 false)
  • 一元运算符:+ "123" → 123(字符串转数字)

五、手写深度比较,模拟 lodash isEqual


六、split()和join0 的区别

  • split(separator):将字符串按分隔符拆分为数组(字符串 → 数组)
    例:'a,b,c'.split(',') → ['a','b','c']
  • join(separator):将数组元素按分隔符拼接为字符串(数组 → 字符串)
    例:['a','b','c'].join(',') → 'a,b,c'


七、数组的 pop push unshift shift 分别做什么?

  • pop():删除数组最后一个元素,返回被删除元素(改变原数组)
  • push():向数组末尾添加元素,返回新长度(改变原数组)
  • unshift():向数组开头添加元素,返回新长度(改变原数组)
  • shift():删除数组第一个元素,返回被删除元素(改变原数组)

八、数组slice和splice区别?

  • slice(start, end):返回数组从 start 到 end(不包含)的子数组,不改变原数组(纯函数)。
    例:[1,2,3].slice(1,2) → [2]
  • splice(start, deleteCount, ...items):从 start 开始删除 deleteCount 个元素,可添加新元素,改变原数组,返回被删除元素。
    例:[1,2,3].splice(1,1,4) → 原数组变为 [1,4,3],返回 [2]

九、 [10,20,30].map(parseInt) 返回结果是什么 ?

结果:[10, NaN, NaN]
解析:

  • map 传递 (元素,索引) 给 parseInt,即:
    parseInt(10, 0) → 10(基数 0 视为 10)
    parseInt(20, 1) → NaN(基数 1 无效)
    parseInt(30, 2) → NaN(30 不是二进制数)

十、ajax 请求 get 和 post 的区别 ?精简回答

  1. 数据位置:get 数据在 URL 中;post 数据在请求体中。
  2. 长度限制:get 受 URL 长度限制;post 无(取决于服务器)。
  3. 缓存:get 可缓存;post 通常不可。
  4. 语义:get 用于获取数据;post 用于提交数据(修改服务器状态)。

九、函数 call 和 apply 的区别

  • 参数传递方式不同
    • call(thisArg, arg1, arg2, ...):逐个传递参数
    • apply(thisArg, [argsArray]):以数组形式传递参数
  • function fn(a, b) { console.log(a + b); }
    fn.call(null, 1, 2); // 3(参数逐个传)
    fn.apply(null, [1, 2]); // 3(参数放数组里)


十一、事件代理(委托)是什么?

定义:将子元素的事件监听委托给父元素,利用事件冒泡触发父元素的监听函数,再通过 event.target 判断具体触发元素。
作用:减少事件监听数量,优化性能;支持动态新增元素的事件处理。

// 父元素代理所有子元素的点击事件
document.getElementById('parent').addEventListener('click', (e) => {if (e.target.tagName === 'LI') { // 判断触发源是子元素LIconsole.log('点击了LI:', e.target.textContent);}
});


十二、闭包是什么,有什么特性?有什么负面影响?

定义:函数嵌套中,内部函数引用外部函数的变量 / 参数,且内部函数被外部访问,形成闭包。
特性

  • 延长外部函数变量的生命周期(不被垃圾回收)
  • 内部函数可访问外部作用域变量
function outer() {let count = 0;return function inner() { // 闭包:inner引用了outer的countcount++;return count;};
}
const fn = outer();
console.log(fn()); // 1(count被保留)
console.log(fn()); // 2

负面影响

  • 变量长期驻留内存,可能导致内存泄漏
  • 过度使用会增加内存消耗,影响性能

十三、如何阻止事件冒泡和默认行为?

  • 阻止冒泡:event.stopPropagation()(IE 低版本用event.cancelBubble = true
  • 阻止默认行为:event.preventDefault()(IE 低版本用event.returnValue = false
  • 同时阻止:事件处理函数中返回false(仅部分框架有效)


十四、查找、添加、删除、移动 DOM 节点的方法 ?

  • 查找:getElementById()querySelector()querySelectorAll()getElementsByClassName()
  • 添加:appendChild()insertBefore()createElement()结合append()
  • 删除:removeChild()remove()(直接删除自身)
  • 移动:先removeChild()appendChild()insertBefore()


十五、如何减少 DOM 操作 ?

  1. 使用文档片段(DocumentFragment)批量操作
  2. 合并 DOM 修改,减少重排重绘
  3. 用虚拟 DOM(如 React)间接操作
  4. 缓存 DOM 查询结果,避免重复查找
  5. 离线更新 DOM(先隐藏再修改)

十六、解释 jsonp 的原理,为何它不是真正的 ajax ?

  • 原理:利用 script 标签不受同源策略限制的特性,通过动态创建 script 标签,请求带回调函数名的跨域接口,服务器返回回调函数包裹的 JSON 数据,实现跨域数据获取。
  • 不是真正的 ajax:ajax 基于 XMLHttpRequest 对象,而 jsonp 基于 script 标签请求;ajax 可发送多种请求方式,jsonp 只能用 GET;ajax 遵循同源策略,jsonp 是规避同源策略的技巧。

十七、document load 和 ready 的区别?

  • ready:DOM 结构加载完成后触发(无需等待样式、图片等资源),可多次触发。
  • load:整个页面(包括 DOM、样式、图片等所有资源)加载完成后触发,仅触发一次。

十八、==和 ===的不同

  • ==:宽松相等,会先进行类型转换再比较值是否相等。
  • ===:严格相等,不进行类型转换,直接比较值和类型是否都相等

十九、函数声明和函数表达式的区别 ?

  • 函数声明:function fn() {},存在变量提升,可在声明前调用。
  • 函数表达式:const fn = function() {},无变量提升,声明前调用会报错。
  • 函数声明必须有函数名,表达式可匿名。

二十、new Object()和 Object.create()的区别?

  • new Object():创建空对象,原型指向Object.prototype,类似{}
  • Object.create(proto):以指定对象为原型创建新对象,若传null则新对象无原型。

二十一、关于 this 的场景题

二十二、关于作用域和自由变量的场景题-1

  • 全局 / 普通函数:指向全局对象(浏览器为 window,Node 为 global)。
  • 对象方法调用:指向调用该方法的对象。
  • 构造函数 (new):指向新创建的实例。
  • call/apply/bind:指向传入的第一个参数。
  • 箭头函数:无自身 this,继承外层作用域的 this。
var a = 10;
function fn() {console.log(a); // 自由变量a,向上查找外层作用域的a → 10
}
function bar() {var a = 20;fn();
}
bar(); 


二十三、判断字符串以字母开头,后面字母数字下划线,长度 6-30

  • 作用域fn 的作用域链为自身 → 全局,bar 的作用域不影响 fn
  • 自由变量fn 中未声明的 a 是自由变量,沿作用域链查找(非调用位置),结果为全局 a=10
const reg = /^[a-zA-Z][a-zA-Z0-9_]{5,29}$/;
// 解析:
// ^[a-zA-Z] → 开头必须是字母
// [a-zA-Z0-9_]{5,29} → 后续5-29个字符(字母/数字/下划线)
// $ → 结束符


二十四、关于作用域和自由变量的场景题-2

    let x = 10;
    function outer() {let x = 20;function inner() {console.log(x); // 自由变量x,查找最近外层作用域的x → 20}inner();
    }
    outer();
    • 作用域嵌套inner 作用域嵌套于 outer,优先访问 outer 中的 x
    • 自由变量查找规则:沿作用域链向上查找(静态作用域,定义时确定,非执行时)。
    • <script>let a = 100;function test() {alert(a);a = 10;alert(a);}test();alert(a);</script>
    • 执行过程分析:
      • 首先,let a = 100 声明了全局变量 a 并赋值为 100
      • 调用 test 函数:
        • 第一个 alert(a):此时在 test 函数作用域内,查找 a,因为函数内没有声明 a,所以向上查找全局作用域的 a,输出 100
        • 然后 a = 10:这里是对全局变量 a 进行赋值,将全局的 a 改为 10
        • 第二个 alert(a):在 test 函数作用域内,a 已经被修改为 10(全局变量),输出 10
      • 函数 test 执行完后,执行 alert(a):此时访问的是全局变量 a,其值已经被改为 10,输出 10
    • 最终输出结果依次为:1001010 。

    二十五、常见正则表达式

    1. 邮政编码

    正则:/\d{6}/
    功能:匹配 6 位数字(符合邮政编码规则)
    优化:若需严格 “仅 6 位数字”,建议加锚点 ^ 和 $ → /^\d{6}$/,避免匹配 “1234567” 里的 “234567”

    2. 小写英文字母

    正则:/^[a-z]+$/
    功能:验证字符串仅包含 1 个及以上小写字母(如 abc 匹配,Abc12 不匹配 )

    3. 英文字母

    正则:/^[a-zA-Z]+$/
    功能:验证字符串仅包含 1 个及以上大小写字母(如 AbC 匹配,123a-b 不匹配 )

    4. 日期格式(2019.12.1 )

    正则:/^\d{4}-\d{1,2}-\d{1,2}$/
    功能:匹配 “年 - 月 - 日” 格式(如 2025-08-08 匹配,2025.08.08 不匹配 )
    问题:未校验日期合法性(如 2025-13-32 也会匹配 ),若需严格校验需更复杂正则或代码逻辑

    5. 用户名

    正则:/^[a-zA-Z]\w{5,17}$/
    功能:

    • 字母开头a-zA-Z )
    • 后续跟 5~17 个 字母、数字、下划线(总长度 6~18 )
      问题:\w 含下划线,若需限制可显式写字符集(如 [a-zA-Z0-9_] 语义更清晰 )

    6. 简单的 IP 地址匹配

    正则:/\d+/
    功能:匹配 “1 个及以上数字”(完全不满足 IP 规则 )
    问题:IP 需 “4 段数字(0 - 255)+ 点分隔”,正确写法示例:

    /^((25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/
    

    (校验每段 0~255 ,且用点分隔 )

    二十六、手写字符串 trim 方法,保证浏览器兼容性?

    function myTrim(str) {if (String.prototype.trim) {return str.trim(); // 利用原生方法}// 不支持原生trim时,用正则移除首尾空白return str.replace(/^\s+|\s+$/g, '');
    }


    二十七、如何获取多个数字中的最大值?

    1. 扩展运算符:Math.max(...[num1, num2, ...])
    2. apply 方法:Math.max.apply(null, [num1, num2, ...])
    3. 遍历比较:初始化 max,循环更新最大值

    二十八、如何用 JS 实现继承 ?

    1. 原型链继承:Child.prototype = new Parent()
    2. 构造函数继承:父类.call (this, 参数)(解决属性继承)
    3. 组合继承:原型链 + 构造函数(兼顾属性和方法)
    4. ES6 class 继承:class Child extends Parent { constructor() { super() } }(语法糖,本质基于原型)

    二十九、如何捕获 JS 程序中的异常 ?

    1.try/catch/finally 语句
    这是最常用的异常捕获方式,适用于同步代码和标记为 await 的异步代

    try {// 可能抛出异常的代码const result = riskyOperation();
    } catch (error) {// 捕获并处理异常(error 包含错误信息)console.error('发生错误:', error.message);
    } finally {// 无论是否发生异常,都会执行的代码(如资源清理)console.log('操作结束');
    }

    2.异步代码的异常处理

    对于 Promise,使用 .catch() 方法

    fetchData().then(data => process(data)).catch(error => console.error('请求失败:', error));

    3.用于捕获未被局部处理的异常,避免程序崩溃

    • 浏览器环境:window.onerror 或 window.addEventListener('error')
    • Node.js 环境:process.on('uncaughtException')
    • Promise 未捕获异常:window.addEventListener('unhandledrejection')(浏览器)或 process.on('unhandledRejection')(Node.js)

    三十、什么是 JSON ?

    JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,具有以下特点:

    语法规则

    • 基于 JavaScript 对象字面量语法,但独立于语言(任何语言均可解析)。
    • 键必须用双引号包裹,值可以是字符串、数字、布尔值、null、数组或对象。
    {"name": "Alice","age": 30,"isStudent": false,"hobbies": ["reading", "coding"]
    }
    1. 用途

      • 前后端数据传输(如 API 请求 / 响应)。
      • 配置文件存储。
      • 数据序列化(将对象转换为字符串便于传输或存储)。
    2. JS 中的 JSON 方法

      • JSON.stringify():将 JS 对象转换为 JSON 字符串。
      • JSON.parse():将 JSON 字符串解析为 JS 对象(若格式错误会抛出异常)。

    三十一、获取当前页面 url 参数

    获取 URL 参数的方案需考虑兼容性、边界情况易用性,以下是进阶实现思路:

    1. 核心实现(解析 URLSearchParams)

    现代浏览器支持 URLSearchParams API,可简洁处理参数:

    function getUrlParams() {const params = new URLSearchParams(window.location.search);const result = {};// 迭代所有参数并转为对象for (const [key, value] of params.entries()) {// 处理多值参数(如 ?ids=1&ids=2)if (result.hasOwnProperty(key)) {result[key] = Array.isArray(result[key]) ? [...result[key], value] : [result[key], value];} else {result[key] = value;}}return result;
    }
    2. 兼容旧环境(手动解析)

    对于不支持 URLSearchParams 的环境(如 IE),可手动解析:

    function getUrlParams() {const search = window.location.search.slice(1); // 去除问号if (!search) return {};const params = {};const pairs = search.split('&');pairs.forEach(pair => {// 处理空参数(如 ?key= 或 ?key)const [key, value = ''] = pair.split('=').map(decodeURIComponent);if (params[key] !== undefined) {params[key] = Array.isArray(params[key]) ? [...params[key], value] : [params[key], value];} else {params[key] = value;}});return params;
    }
    3. 高级特性扩展
    • 类型转换:自动将数字、布尔值、null 转换为对应类型:
      function convertValue(value) {if (value === 'null') return null;if (value === 'true') return true;if (value === 'false') return false;if (!isNaN(Number(value))) return Number(value);return value;
      }

    单例模式:避免重复解析,缓存结果:

    const urlParams = (() => {// 解析逻辑(同上)return parsedParams;
    })();
    
    4. 面试加分点
    • 提及 URL 构造函数:new URL(window.location.href).searchParams
    • 注意参数编码问题(decodeURIComponent 处理空格、特殊字符)。
    • 区分 location.search(查询参数)与 location.hash(哈希值)。
    • 对于 SPA 路由(如 React Router),需通过路由库的 API 获取参数(如 useSearchParams)。

    三十二、手写深拷贝

    function deepClone(obj = {}) {// 1. 基本数据类型或者 null,直接返回(递归的终止条件)//    typeof null 的结果是 'object',所以单独判断 obj === nullif (typeof obj!== 'object' || obj === null) {return obj;}// 2. 初始化用于存储深拷贝结果的容器let result;// 如果是数组,就初始化一个空数组if (obj instanceof Array) {result = [];} else {// 不是数组,就初始化一个空对象result = {};}// 3. 遍历原对象/数组的可枚举自有属性(不遍历原型链上的属性)for (let key in obj) {// 保证 key 是当前对象自身的属性,而不是继承自原型链的if (obj.hasOwnProperty(key)) {// 递归调用 deepClone,对属性值进行深拷贝// 把拷贝后的结果赋值给新对象/数组对应的属性result[key] = deepClone(obj[key]);}}// 4. 返回深拷贝后的结果return result;
    }

    三十三、介绍-下 RAF requestAnimateFrame ?

    requestAnimationFrame是浏览器提供的用于同步动画渲染的 API,主要特点:

    1. 工作原理

      • 告诉浏览器 "我要执行动画",浏览器会在下一次重绘前调用指定回调函数
      • 回调函数接收一个时间戳参数(performance.now () 返回值),表示当前执行时间
    2. 优势

      • 自动匹配浏览器刷新率(通常 60fps),避免过度绘制导致的性能浪费
      • 当页面处于后台或标签页不可见时,会暂停执行,节省 CPU 资源
      • 比 setTimeout/setInterval 更精准,避免因主线程繁忙导致的动画卡
    3. 基本用法

      let progress = 0;function animate(timestamp) {progress += 1;if (progress < 100) {// 更新动画状态(如DOM样式)element.style.left = progress + 'px';// 继续请求下一帧requestId = requestAnimationFrame(animate);}
      }// 启动动画
      const requestId = requestAnimationFrame(animate);// 取消动画(如需中途停止)
      cancelAnimationFrame(requestId);
      

    4. 适用场景

      • DOM 动画(如位置、大小、透明度变化)
      • Canvas/SVG 动画
      • 数据可视化动态效果

    三十四、前端性能如何优化,一般从哪几个方面考虑 ?

    前端性能优化需从用户体验技术实现双维度考虑,核心目标是:减少加载时间、提升交互响应速度、降低资源消耗。

    1. 网络层优化
    • 资源加载策略

      • 实施 HTTP/2(多路复用)或 HTTP/3(QUIC 协议)
      • 静态资源 CDN 分发,减少跨地域延迟
      • 资源压缩:JS/CSS 压缩(Terser/CSSNano)、图片压缩(WebP/AVIF 格式)
      • 资源合并:合理合并 JS/CSS(避免过度合并导致缓存失效)
    • 缓存机制

      • 强缓存(Cache-Control/Expires):长期不变资源(如图片、库文件)
      • 协商缓存(ETag/Last-Modified):频繁更新但不常变资源
      • Service Worker:实现离线缓存和请求拦截
    • 预加载策略

      • preload:高优先级资源(如首屏关键 CSS/JS)
      • prefetch:低优先级资源(如后续页面可能用到的资源)
      • 懒加载:图片、视频、组件(基于 IntersectionObserver)
    2. 渲染层优化
    • DOM 操作优化

      • 减少重排(Reflow)和重绘(Repaint):
        • 使用 DocumentFragment 批量操作 DOM
        • 避免频繁读取 offsetTop 等触发重排的属性
        • 将频繁变化的元素设为will-change: transform(触发 GPU 加速)
      • 虚拟列表(Virtual List):处理大数据列表渲染
    • CSS 优化

      • 避免复杂选择器(如嵌套过深的后代选择器)
      • 减少使用@import(阻塞并行下载)
      • 关键 CSS 内联到 HTML 头部,非关键 CSS 异步加载
    • JavaScript 优化

      • 代码分割(Code Splitting):基于路由或组件动态导入
      • 避免长任务阻塞主线程:将耗时操作放入 Web Worker
      • 事件委托:减少事件监听器数量
      • 使用 requestAnimationFrame 处理动画,避免使用 setTimeout
    3. 代码层优化
    • 算法与数据结构:优化复杂逻辑的时间复杂度(如避免 O (n²) 循环)
    • 树摇(Tree Shaking):移除未使用的代码(依赖 ES6 模块)
    • 第三方库优化:按需引入(如 lodash-es 代替完整 lodash)
    • 内存管理:及时清除定时器、事件监听器,避免闭包导致的内存泄漏
    4. 监控与量化
    • 性能指标监控:
      • 核心 Web 指标(LCP、FID、CLS)
      • 传统指标(白屏时间、首屏时间、DOMContentLoaded)
    • 性能分析工具:Lighthouse、Chrome Performance 面板
    • 建立性能预算(Performance Budget):限制资源大小和加载时间
    面试加分点
    • 结合具体业务场景谈优化(如电商首页 vs 管理系统的不同策略)
    • 提及新兴技术(如 HTTP/3、Web Assembly、边缘计算)
    • 强调性能优化的 "性价比":避免过度优化导致的维护成本上升

    http://www.lryc.cn/news/617610.html

    相关文章:

  • Vibe Coding 自然语言驱动 AI 编程方式
  • Redis类型之Hash
  • AI产品经理手册(Ch12-16)AI Product Manager‘s Handbook学习笔记
  • Vue 中的 Class 与 Style 绑定详解1
  • lesson35:数据库深度解析:从概念到MySQL实战学习指南
  • 面试实战 问题二十三 如何判断索引是否生效,什么样的sql会导致索引失效
  • 【排序算法】⑥快速排序:Hoare、挖坑法、前后指针法
  • 微信小程序常用 API
  • Seata
  • 小杰python高级(three day)——matplotlib库
  • Spark 优化全攻略:从 “卡成 PPT“ 到 “飞一般体验“
  • Vlanif 实验
  • 第16届蓝桥杯Python青少组_省赛_中/高级组_2025年5月真题
  • 国企社招 | 中国邮政2025年社会招聘开启
  • 腾讯前端面试模拟详解
  • Java 之抽象类和接口
  • AIStarter修复macOS 15兼容问题:跨平台AI项目管理新体验
  • docker是什么以及镜像命令详解
  • C++模板的补充
  • 【读代码】微软开源Agentic-RAG深度解析
  • Profile.vue组件详细解析
  • SDH 和 OTN 的帧结构对比
  • 3.数据类型和类型装换
  • Spring-Security-5.7.11升级6.5.2
  • Unity笔记(五)知识补充——场景切换、退出游戏、鼠标隐藏锁定、随机数、委托
  • 前端面试:promise...then与asnyc ...await
  • 简单了解MongoDB数据存储
  • ‌太钢建材:筑就未来,品质见证
  • 软考倒计时 巧用芝麻倒计时软件 助力高效备考 有效提升备考效率
  • DNS(域名系统)