前端

一、cookie、sessionStorage 和 localStorage 的区别,以及在实际开发中你是如何选择使用它们的?比如用户登录状态、保存用户偏好设置这类场景,你会怎么处理?

  • Cookie:

    • 容量小(4KB左右);
    • 每次请求都会自动带到服务器(适合做用户身份识别);
    • 可以设置过期时间;
    • 常用于服务端读取,比如存储 tokensession id 等;
    • 可通过 document.cookie 操作,但不太方便。
  • localStorage:

    • 容量大(5MB左右);
    • 只存在于浏览器端,不会自动随请求发送;
    • 数据持久化,除非手动清除;
    • 常用于保存用户设置、主题偏好等不敏感数据。
  • sessionStorage:

    • 类似 localStorage,但生命周期是"当前标签页";
    • 页面关闭就被清除;
    • 多个标签页互相隔离;
    • 适合临时数据,比如表单填写进度。

举例说明(加分项 🔥):

比如在一个用户登录系统中,我通常会用:

  • Cookie 来存储 token,这样如果后台需要身份验证,可以从请求头中拿到;
  • localStorage 存用户的偏好设置(比如主题是深色还是浅色),因为这种数据希望在用户回访时还存在;
  • sessionStorage 可能会用来暂存一些非持久性的表单数据,比如用户填写多步表单,还没提交就刷新页面,数据还能保留。

二、逻辑题-一副扑克拿掉大小王问最多几个人能够抽到相同的两个花色

题目简化:

一副扑克牌去掉大小王,即只剩下 52 张牌 (4 种花色,每种花色 13 张)。 问:最多有多少个人 ,每人抽 两张牌且颜色相同

  1. 每个花色有 13 张牌

公式:C(n, k) 表示从 n 个元素中选 k 个,不考虑顺序。 (也就是说,问题简化-从 13 张同花色的牌里选出 2 张,不管顺序(♠A + ♠K 跟 ♠K + ♠A 是一样的组合),有多少种选法?)

  • 那么一个花色中,最多能组成几对牌?

  • 从 13 张中选 2 张:

    C(13,2)=13×12/2=78

  • 所以一个花色能抽出 78 个"同花色的两张牌"。

  1. 总共 4 种花色:

    4×78=312 对

  2. 问题问的是"最多几个人能够抽到两个相同花色的牌",每人抽一对

    最多可以满足312个人。

三、逻辑题-如何计算黄河一天流多少吨水

核心公式:流量(吨/天)=流速(m/s)×断面面积(m²)×86400(秒/天)×1000(kg/m³)÷1000

⚠️ 注意:因为 1 立方米水 ≈ 1000 千克 = 1 吨,所以最后可以直接乘 86400 × 断面流量。

🚀步骤拆解:

1️⃣ 假设/查到黄河某一段的参数(比如兰州段):

  • 平均流速:约 2 米/秒
  • 断面面积 :比如河宽 200 米、平均水深 3 米
    → 面积 = 200 × 3 = 600 m²

2️⃣ 算每秒流量(单位:立方米/秒):

Q=2 (m/s)×600 (m²)=1200 m³/s


3️⃣ 算每天总流量(单位:吨/天):

每天秒数=24×60×60=86400 秒

每日总吨数=1200×86400=103,680,000 吨


🎯答案(估算):

黄河某一段(如兰州)大概一天流 1亿吨左右的水


四、跨页面通信-比如说我在商品页有一个id我在其他页面怎么拿到

  1. 父传子props, 子传父 自定义事件 <math xmlns="http://www.w3.org/1998/Math/MathML"> e m i t 、 emit、 </math>emit、on
  2. 获取父组件、子组件实例 <math xmlns="http://www.w3.org/1998/Math/MathML"> p a r e n t 、 parent、 </math>parent、children
  3. ref 可以获取组件实例或者dom元素
  4. vuex
  5. event bus
  6. localStorage / sessionStorage / cookie
比如说我在商品页有一个id我在其他页面怎么拿到?
  1. URL 传参(最常见)
js 复制代码
// 跳转时:
window.location.href = `/checkout?productId=123`;

// 新页面获取:
const urlParams = new URLSearchParams(window.location.search);
const productId = urlParams.get("productId");

2.localStorage / sessionStorage 存取

js 复制代码
// 存入
localStorage.setItem("productId", "123"); // 或 sessionStorage

// 获取
const productId = localStorage.getItem("productId");

3.状态管理

js 复制代码
// 例如在 Zustand 中设置:
store.setState({ productId: "123" });

// 在其他页面组件中读取:
const productId = useStore((state) => state.productId);

4.通过路由 params(前端路由,比如 React Router、Vue Router)

js 复制代码
// 商品页-路由
/product/:id

// 在跳转页面用 useParams 获取
const { id } = useParams();

五、vue组件通信

  1. 父传子props, 子传父 自定义事件 <math xmlns="http://www.w3.org/1998/Math/MathML"> e m i t 、 emit、 </math>emit、on
  2. 获取父组件、子组件实例 <math xmlns="http://www.w3.org/1998/Math/MathML"> p a r e n t 、 parent、 </math>parent、children
  3. ref 可以获取组件实例或者dom元素
  4. vuex
  5. event bus

六、http状态码

200 请求成功 204 没有返回内容

301 被请求的资源永久的移动到了新的地址(永久重定向) 302 被请求的资源临时移动到新地址(临时重定向) 304 被请求的资源未被更新

400 请求出错 401 请求权限未验证 403 没有权限 404 被访问的资源不存在 405 不允许的http请求

500 服务器内部错误 503 服务器无法处理该请求

七、h5语义化-好处

header、nav、main、section、article、aside、footer

好处:

  1. 提升代码可读性
  2. 有利于SEO优化

八、webpack和vite的区别

Webpack 是打包为主,构建时加载;Vite 是原生 ESM 开发,按需加载。Vite 更快、配置更轻,适合现代开发。

九、vue2有用过吗和 vue3的区别

  1. vue3采用ts编写源码
  2. vue3采用组合式API,vue2采用响应式API
  3. 响应式原理不同,vue3采用proxy代理,vue2采用Object.definedProproty
  4. vue3支持多个根标签
  5. vue3采用了setup代替了vue2中的beforeCreate、created

十、vue响应式

Vue2 用 Object.defineProperty,只能劫持已有属性;Vue3 用 Proxy,可以完全代理对象。

vue3使用proxy优势:可以深度监听数组和对象。

十一、前端实现动画的方式有哪些?

实现方式 示例/工具 优点 场景推荐
CSS 动画 transition, animation 简洁、性能好、硬件加速 简单的过渡、hover 动效等
JS 动画(手动) setInterval, requestAnimationFrame 控制灵活 复杂逻辑、与数据联动的动画
CSS-in-JS React/Vue 中的 @keyframesstyle 动画 框架集成方便 React/Vue 等 SPA 应用中常见
第三方库 GSAP、Anime.js、Velocity.js 高级控制、跨浏览器兼容 高质量动画、交互复杂动画
Canvas 动画 canvas API 可绘制图形、帧控制好 游戏、粒子动画、数据可视化等
SVG 动画 animate, SMIL 或 JS 操作 可缩放、适合矢量图 图标动效、路径动画
WebGL 动画 Three.js、Babylon.js 超强性能,适合 3D 动画 游戏、3D 可视化、高性能场景
Framer Motion(React) React 动画库 简洁语法、组件化动画 React 项目中现代、响应式动画

用过 requestAnimationFrame 吗?跟 setTimeout 有什么区别?

  • requestAnimationFrame告诉浏览器在下一帧 执行你提供的回调函数,用来创建平滑、高性能动画
  • 浏览器优化性能,不活跃 tab 不执行,省资源
  • 相比 setTimeout(fn, 16) 更流畅、不抖动

十二、rem/em/px/rpx/vw/vh/%的区别

单位 参考基准 特点 常见场景
px 固定像素(绝对单位) 不随设备缩放,最常用 常规布局、精细控件
em 相对于当前元素的字体大小 会继承叠加,不易控 字体、间距(嵌套注意)
rem 相对于根元素(html)的字体大小 易于统一管理,推荐用于响应式 移动端、字体大小
% 相对父元素 灵活、常配合布局使用 宽高、padding
vw 相对于视口宽度(1vw = 1%) 适配宽度,响应式好 宽度适配、全屏模块
vh 相对于视口高度(1vh = 1%) 适配高度,满屏容器 高度满屏组件
rpx 相对于设备宽度(750 = 100%) 微信小程序专用 小程序布局

十三、css选择器

类型 示例 说明
基础选择器 .class #id div 按类名、ID、标签选择
组合选择器 div p div > p 选择多个元素之间的关系
属性选择器 [type="text"] 根据属性选择元素
伪类选择器 :hover :first-child 根据状态或结构选择
伪元素选择器 ::before ::after 选中元素的一部分(虚拟元素)
群组选择器 h1, h2, h3 多个元素统一设置样式
否定选择器 :not(.active) 排除某些元素

十四、css隐藏元素

方法 是否占位 是否可点击 是否渲染在 DOM 中 说明
display: none ❌ 不占位 ❌ 不可点击 ✅ 在 DOM 中 最常用,彻底移除布局
visibility: hidden ✅ 占位 ❌ 不可点击 ✅ 在 DOM 中 看不到但占空间
opacity: 0 ✅ 占位 ✅ 可点击 ✅ 在 DOM 中 透明但仍可交互
width: 0; height: 0 ✅ 占位 ✅ 可点击 ✅ 在 DOM 中 通常用于隐藏输入框等
position: absolute; left: -9999px ❌ 不占位 ✅ 可点击 ✅ 在 DOM 中 移出视口,仍存在
clip-path: inset(0 0 0 0) + opacity: 0 高级隐藏,配合动画
hidden 属性(HTML) ❌ 不占位 ❌ 不可点击 ✅ 在 DOM 中 类似 display: none

十五、行内元素和块级元素的区别,和分别有哪些?

属性 块级元素(Block) 行内元素(Inline)
是否独占一行 ✅ 是 ❌ 否,和其他元素同行
是否可设置宽高 ✅ 可以 ❌ 不可以(width/height 无效)
默认宽度 100%(占满父容器) 内容撑开的宽度
包含内容 块级元素和行内元素 行内元素
常见用途 结构布局、容器 内容修饰、小部件

十六、position:fixed 和 positon: absolute 区别点

position: absolute 相对于最近的"定位祖先"定位,而 position: fixed 是**相对于视口(viewport)**定位,滚动页面时不会移动。

有一个父元素盒子 div设置transfrom:translate,然后子元素是position:fixed,这时候子元素相对于哪个位置定位的?

当父元素设置了 transform(如 transform: translate(...)),它会创建一个新的"包含块(containing block)" ,此时子元素的 position: fixed相对于这个父元素进行定位而不是视口

  • 通常 position: fixed 是**相对于浏览器视口(viewport)**定位。

  • 但如果它的祖先元素创建了新的"图形上下文(containing block) ",比如设置了:

    • transform
    • filter
    • perspective
    • will-change
    • contain
    • backdrop-filter
    • translate(新版语法)

👉 那么子元素的 fixed 会"脱离视口",变成相对于这个祖先元素定位!

十七、Object的静态方法有哪些?

Object 的静态方法主要用于对象的创建、拷贝、属性定义、遍历等,常见的有 assignkeysvaluesentriescreatedefinePropertyfreezeis 等等。

获取属性相关:

方法名 作用
Object.keys(obj) 获取可枚举自身属性名数组
Object.values(obj) 获取可枚举自身属性值数组
Object.entries(obj) 获取可枚举自身属性的键值对
Object.getOwnPropertyNames(obj) 获取所有自身属性名(包括不可枚举)
Object.getOwnPropertySymbols(obj) 获取所有 Symbol 类型属性名

判断类:

方法名 作用
Object.is(val1, val2) 判断是否"严格相等"(类似 ===,但处理 NaN-0 更精准)
Object.hasOwn(obj, key) 检查对象自身是否含有某属性(ES2022,推荐)

创建类的方法:

方法名 作用
Object.create(proto) 以指定原型创建新对象
Object.assign(target, ...) 浅拷贝、合并对象
Object.fromEntries(entries) 把键值对数组转成对象(ES10)

属性操作类:

方法名 作用
Object.defineProperty(obj, key, descriptor) 定义单个属性(可控制 writable, enumerable 等)
Object.defineProperties(obj, descriptors) 批量定义属性
Object.getOwnPropertyDescriptor(obj, key) 获取某属性的描述符
Object.getOwnPropertyDescriptors(obj) 获取所有属性的描述符(ES8)

控制对象状态类:

方法名 作用
Object.freeze(obj) 冻结对象(不可增删改)
Object.seal(obj) 密封对象(不可新增,可改值)
Object.preventExtensions(obj) 禁止新增属性
Object.isFrozen(obj) 检查是否被冻结
Object.isSealed(obj) 检查是否被密封
Object.isExtensible(obj) 检查是否可扩展

原型链操作类:

方法名 作用
Object.getPrototypeOf(obj) 获取对象原型
Object.setPrototypeOf(obj, proto) 设置对象原型(不推荐频繁使用)

十八、Object.assign

Object.assign() 用于将一个或多个源对象的可枚举属性 复制到目标对象中,返回目标对象。常用于对象合并、浅拷贝、默认值合并等场景。

基本语法:

js 复制代码
Object.assign(target, ...sources);
  • target:目标对象(被修改并返回)
  • sources:一个或多个源对象

示例

  1. 合并对象
js 复制代码
const a = { name: '小明' };
const b = { age: 20 };
const result = Object.assign({}, a, b);
console.log(result); // { name: '小明', age: 20 }
  1. 修改目标对象
js 复制代码
const target = { a: 1 };
const source = { b: 2 };
Object.assign(target, source);
console.log(target); // { a: 1, b: 2 }(target 被改了)
  1. 浅拷贝
js 复制代码
const obj1 = { nested: { x: 1 } };
const obj2 = Object.assign({}, obj1);

obj2.nested.x = 999;
console.log(obj1.nested.x); // 999 ❗️(浅拷贝,嵌套引用的是同一个对象)
  1. 会覆盖同名属性,右边覆盖左边
js 复制代码
Object.assign({ a: 1 }, { a: 2 }); // { a: 2 }
  1. 会跳过不可枚举和原型属性
js 复制代码
const obj = Object.create({ inherited: 123 });
obj.visible = 1;
Object.assign({}, obj); // 只复制 visible,不复制 inherited

十九、浅拷贝和深拷贝的区别。怎么实现浅拷贝、深拷贝

  • 浅拷贝:只拷贝对象的第一层属性,如果属性值是引用类型,拷贝的是引用地址。
  • 深拷贝:不仅拷贝对象本身,也递归拷贝所有嵌套的引用类型属性 ,两个对象完全独立

浅拷贝

js 复制代码
const obj = {
    a: 1,
    b: {
        c: 2
    }
}
// 实现方法1
const resObj = { ...obj }

// 实现方法2
const resObj = Object.assign({}, obj)

// 实现方法3
function light_copy(obj) {
    let resObj = {}
    Object.keys(obj).forEach(t => resObj[t] = obj[t])
    return resObj
}

深拷贝

js 复制代码
const obj = {
    a: 1,
    b: {
        c: 2
    }
}
// 实现方法1
const resObj = JSON.parse(JSON.stringify(obj))

// 实现方法2
import _ from 'lodash';

const deepCopy = _.cloneDeep(obj);

// 实现方法3
function deep_copy1(obj) {
    if (typeof obj !== 'object' || obj === null) return obj;
    let resObj = Array.isArray(obj) ? [] : {}
    for (const key in obj) {
        resObj[key] = deep_copy1(obj[key])
    }
    return resObj
}

// 实现方法4 - 支持循环引用(坑)
const obj = { a: 1, b: 2 };
obj.c = obj; // c 指向 obj 自己
此时使用方法3,会死循环 💥!函数一直递归下去,因为:
-   `obj.c = obj`,它指向自己。
-   `deep_copy1(obj)` → `deep_copy1(obj.c)` → `deep_copy1(obj)` → ... 无限递归。

function deep_copy2(obj, cache = new WeakMap()) {
    if (typeof obj !== 'object' || obj === null) return obj;
    
    // 如果这个对象已经被拷贝过,直接返回这个对象
    if (cache.has(obj)) return catch.get(obj);
    
    let resObj = Array.isArray(obj) ? [] : {}
    catch.set(obj, res) // 缓存
    
    for (const key in obj) {
        resObj[key] = deep_copy2(obj[key], cache)
    }
    
    return resObj
}

对数组浅拷贝,有哪些方式?

方法 写法(const arr = [1, 2, 3];) 说明
slice() const copy = arr.slice(); 常规经典
[...arr] const copy = [...arr]; 最推荐、最简洁
Array.from() const copy = Array.from(arr); 用于类数组也很好
concat() const copy = [].concat(arr); ES5 写法
map(x => x) const copy = arr.map(x => x); 不推荐,仅供了解

二十、setTimeout / setInterval 准时么 为什么?

不准时。它们的定时机制是基于 JavaScript 的事件循环(Event Loop) , 并不是"到了时间点就执行",而是"等主线程空了,才有机会执行。

二十一、Promise

PromiseJavaScript 异步编程的核心部分,它的主要作用是处理异步操作,让代码更加清晰、可维护。

Promise 的三个状态:

  1. Pending(待定)

    • 初始状态。即 Promise 创建时的状态,表示异步操作正在进行中。
  2. Fulfilled(已兑现)

    • 当异步操作成功执行时,Promise 进入此状态。此时可以通过 then() 方法获取到结果。
  3. Rejected(已拒绝)

    • 当异步操作失败时,Promise 进入此状态。此时可以通过 catch() 方法获取到错误信息。

Promise 构造函数是同步还是异步?

Promise 的构造函数(new Promise()是同步执行的,但它的执行内容是异步的。

Promise.all

Promise.all() 方法用于将多个 Promise 对象包装成一个新的 Promise,当所有的 Promise 都变成 fulfilled 状态时,它才会触发 then(),如果有一个 Promiserejected ,整个 Promise.all() 会立即进入 rejected 状态。

js 复制代码
function myPromiseAll(promises) {
  return new Promise((resolve, reject) => {
    let results = [];
    let completed = 0;

    for (let i = 0; i < promises.length; i++) {
      // 确保每个 promise 执行的顺序和传入的顺序一致
      promises[i].then((value) => {
        results[i] = value;
        completed++;
        if (completed === promises.length) {
          resolve(results);
        }
      }).catch((error) => {
        reject(error); // 一旦有一个失败,直接 reject
      });
    }
  });
}

Promise实现红绿灯不断交替亮灯的逻辑

用Promise实现红绿灯不断交替亮灯的逻辑

红灯3秒亮一次,绿灯1秒亮一次,黄灯2秒亮一次;

js 复制代码
function red() {
  console.log('红灯亮');
}

function green() {
  console.log('绿灯亮');
}

function yellow() {
  console.log('黄灯亮');
}

function changeLight(light, time) {
    return new Promise(resolve => {
        // 亮灯
        light()
        // 等待指定时间后 resolve
        setTimeout(() => {
            resolve()
        }, time * 1000)
    })
}

async function run(count = 100) {
    let i = 0;
    while (i < count) {
    i++
    await changeLight(red, 3)
    await changeLight(yellow, 2)
    await changeLight(green, 1)
    }
}
run()

二十二、解析 URL 参数,带嵌套结构

给定如下 URL:

js 复制代码
const url = "https://www.test.com/index?name=Tom&age=18&hobby=reading&hobby=coding&location[city]=Beijing&location[country]=China";

要求:

请你解析出 URL 中 ? 后的内容,返回一个结构如下的对象:

js 复制代码
{
  name: "Tom",
  age: "18",
  hobby: ["reading", "coding"],
  location: {
    city: "Beijing",
    country: "China"
  }
}

解答:

js 复制代码
function parseUrlParams(url) {
  // 按照?截取后面的内容
  const str = url.split("?")[1];
  // 按照&拆分
  const arr = str.split("&");
  // 构建一个obj
  let obj = {};
  // 循环处理
  for (let i = 0; i < arr.length; i++) {
    // 解析每一项的key和value
    const [rawKey, rawVal] = arr[i].split("=");
    const key = decodeURIComponent(rawKey);
    const val = decodeURIComponent(rawVal);
    // 判断是否为对象结构(单层结构)(例如 location[city]=Beijing)
    // if (key.includes("[") && key.includes("]")) {
    //  const start = key.indexOf("[");
    // const end = key.indexOf("]");
    // const parentKey = key.slice(0, start); // 比如 location
    // const childKey = key.slice(start + 1, end); // 比如 city
    // if (!obj[parentKey]) obj[parentKey] = {};
    // obj[parentKey][childKey] = val;
    // } 
     // 判断是否为对象结构(多层结构)
    if (key.includes("[") && key.includes("]")) {
      const keys = key.split(/\[|\]/).filter(Boolean);  // 将 "location[address][city]" 拆解成 ["location", "address", "city"] 
      let current = obj;  // 使用一个临时变量来遍历 obj
      for (let j = 0; j < keys.length - 1; j++) { // 遍历 keys 数组,逐层创建对象
        if (!current[keys[j]]) {
          current[keys[j]] = {};  // 创建嵌套对象
        }
        current = current[keys[j]];  // 深入到下一层
      }
      current[keys[keys.length - 1]] = val;// 最后将值赋给最终的子属性
    } 
    else if (obj[key]) {
      // 判断数组(例如 hobby=reading&hobby=coding)
      if (!Array.isArray(obj[key])) {
        obj[key] = [obj[key]]; // 将原来的值变为数组
      }
      obj[key].push(val);
    } else {
      obj[key] = val;
    }
  }
  return obj;
}
parseUrlParams(url);

二十三、React children

在 React 中,每个组件都可以有 children 作为其内容,children 可以是任何有效的 React 元素(如字符串、数字、JSX 元素、其他 React 组件等)。

children 的常见用法:

  1. 组件嵌套

    你可以在一个组件内部使用其他组件作为 children,这使得组件可以非常灵活地组合和重用。

    js 复制代码
    function Wrapper({ children }) {
      return (
        <div className="wrapper">
          {children}
        </div>
      );
    }
    
    function App() {
      return (
        <Wrapper>
          <h1>This is inside a wrapper</h1>
        </Wrapper>
      );
    }
  2. 默认值

    如果没有传递 children,你可以为它设置默认值:

    js 复制代码
    function Box({ children = "Default content" }) {
      return (
        <div className="box">
          {children}
        </div>
      );
    }
  3. React.Children 工具:

    React 提供了一些工具来处理 children,例如 React.Children.map()React.Children.toArray() 等,可以让你更方便地操作 children(特别是当 children 是一个数组或多个元素时)。

    js 复制代码
    function List({ children }) {
      return (
        <ul>
          {React.Children.map(children, (child) => (
            <li>{child}</li>
          ))}
        </ul>
      );
    }
    
    function App() {
      return (
        <List>
          <span>Item 1</span>
          <span>Item 2</span>
          <span>Item 3</span>
        </List>
      );
    }
  4. 条件渲染:

    children 也可以用来进行条件渲染:

    js 复制代码
    function Box({ children }) {
      return (
        <div className="box">
          {children ? children : <p>No content available</p>}
        </div>
      );
    }
  5. React Fragment 和 Children:

    如果你需要返回多个元素而不引入额外的 DOM 节点,可以使用 React.Fragment 来包裹多个 children 元素。

    js 复制代码
    function App() {
      return (
        <React.Fragment>
          <h1>Title</h1>
          <p>Description</p>
        </React.Fragment>
      );
    }

二十四、什么时候在react必须使用key

循环/动态渲染列表时

js 复制代码
const items = ['apple', 'banana', 'orange'];

function FruitList() {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={item}>{item}</li>
      ))}
    </ul>
  );
}

二十五、setState

  • setState 是 React 类组件中用来更新状态的函数。

  • setState 是异步的,因此在更新状态后直接访问 state 可能不会立即得到更新后的值。

  • 使用 setState 时可以传递回调函数,确保在状态更新完成后执行某些操作。

  • 当状态更新依赖于先前的状态时,使用函数式的 setState

  • 在函数组件中,useState 替代了 setState

二十六、归并排序

归并排序(Merge Sort)是一种有效的排序算法,采用了分治法(Divide and Conquer)思想。它通过将数组分成两个子数组,分别对这两个子数组进行排序,最后将它们合并成一个有序的数组。

归并排序的基本步骤:

  1. 分解:将待排序的数组分成两半,分别对两个子数组进行递归排序。
  2. 合并:合并两个已排序的子数组,得到一个有序的数组。

归并排序的工作原理:

  • 首先将数组不断地分割成两个子数组,直到每个子数组只有一个元素或为空。
  • 然后通过合并两个已排序的子数组来得到更大的有序数组。
  • 递归地合并,直到得到一个完整的有序数组。
js 复制代码
// 归并排序的主函数
function mergeSort(arr) {
  // 如果数组长度小于2,直接返回
  if (arr.length <= 1) {
    return arr;
  }

  // 找到数组的中间位置
  const mid = Math.floor(arr.length / 2);

  // 将数组分成两部分
  const left = mergeSort(arr.slice(0, mid));  // 左半部分
  const right = mergeSort(arr.slice(mid));    // 右半部分

  // 合并两个已排序的数组
  return merge(left, right);
}

// 合并两个已排序的数组
function merge(left, right) {
  let result = [];
  let i = 0;
  let j = 0;

  // 比较两个数组中的元素,逐个添加到结果数组中
  while (i < left.length && j < right.length) {
    if (left[i] < right[j]) {
      result.push(left[i]);
      i++;
    } else {
      result.push(right[j]);
      j++;
    }
  }

  // 如果左数组还有剩余元素,添加到结果数组中
  while (i < left.length) {
    result.push(left[i]);
    i++;
  }

  // 如果右数组还有剩余元素,添加到结果数组中
  while (j < right.length) {
    result.push(right[j]);
    j++;
  }

  return result;
}

// 测试
const arr = [38, 27, 43, 3, 9, 82, 10];
console.log(mergeSort(arr)); // 输出已排序的数组

二十七、怎么获取微信小程序用户信息,设备信息

要获取微信小程序用户的个人信息,需要使用 wx.getUserProfile() 或者 wx.getUserInfo()(不推荐使用) API。

wx.getUserInfo()(不推荐使用)

wx.getUserInfo() 已经被微信废弃,不推荐使用,主要是因为此方法获取的信息不如 wx.getUserProfile() 那么详细,并且没有用户授权的提示框,容易导致隐私问题。微信官方建议使用 wx.getUserProfile() 替代。

获取设备信息

微信小程序提供了多种方法来获取设备的硬件信息,包括操作系统、屏幕尺寸、网络状态等。常见的获取设备信息的 API 有:

获取系统信息:wx.getSystemInfoSync()wx.getSystemInfo()

  • wx.getSystemInfoSync():同步获取系统信息。
  • wx.getSystemInfo():异步获取系统信息。

获取网络类型:wx.getNetworkType()

获取当前设备的网络状态,例如 Wi-Fi、4G、3G、2G、无网络等。

获取设备存储信息:wx.getStorageInfoSync()wx.getStorageInfo()

  • wx.getStorageInfoSync():同步获取本地存储的相关信息。
  • wx.getStorageInfo():异步获取本地存储的相关信息。

二十八、float使用注意

  • 清除浮动 :使用 clearfixclear 来防止父元素塌陷。

  • 浮动脱离文档流:浮动元素脱离文档流,不会影响其他元素的布局。

  • 父元素高度塌陷:如果父元素只包含浮动元素,可能会出现父元素高度为零的情况,需使用清除浮动。

  • 浮动元素宽度 :浮动元素的宽度默认是它内容的宽度,需显式设置 width

  • 多列布局 :浮动元素有可能导致布局不适应,推荐使用 flexboxgrid

二十九、flex

flex 布局的核心思想是:容器的子元素(flex items)能够自动适应父容器的大小并根据可用空间进行分配

三十、ajax 和 fetch区别

特性 AJAX(XMLHttpRequest) Fetch API
支持的技术 基于 XMLHttpRequest,回调函数模式 基于 Promise,更加简洁和现代
异步操作 使用回调函数处理异步操作,容易导致回调地狱 使用 Promise 处理,避免回调地狱
默认错误处理 会自动处理 HTTP 错误(如 404、500),但是只能通过 onerror 等来处理 不会自动处理 HTTP 错误,需要手动检查 response.ok
数据处理 数据处理方式复杂,通常需要手动解析返回数据 response.json()response.text() 等方法支持解析返回数据
跨域支持 需要设置 CORS 头部,且在一些浏览器中可能会遇到问题 默认支持 CORS,简单的跨域处理非常方便
请求配置 需要手动设置请求头、请求方法等 提供了更灵活的配置选项,如 modeheadersbody
兼容性 所有现代浏览器和老版本浏览器支持 现代浏览器支持,老版本浏览器需要 polyfill

二十四、继承

继承是面向对象编程(OOP)中的一个核心概念,它允许一个类继承另一个类的属性和方法。 JavaScript 也支持继承机制,允许一个对象(子类)继承另一个对象(父类)的属性和方法。

二十五、多列等高的布局

  • flex布局
js 复制代码
<div class="container">
  <div class="item">Column 1</div>
  <div class="item">Column 2</div>
  <div class="item">Column 3</div>
</div>
.container {
  display: flex; /* 激活 flexbox 布局 */
  justify-content: space-between; /* 子元素均匀分布 */
}

.item {
  flex: 1; /* 让每个 item 均等分配可用空间 */
  margin: 0 10px; /* 可以根据需求添加间隔 */
  background: #f0f0f0;
  padding: 20px;
  border: 1px solid #ddd;
}
  • Grid布局
js 复制代码
<div class="grid-container">
  <div class="grid-item">Column 1</div>
  <div class="grid-item">Column 2</div>
  <div class="grid-item">Column 3</div>
</div>
.grid-container {
  display: grid;
  grid-template-columns: repeat(3, 1fr); /* 设置3列,每列占1份空间 */
  gap: 20px; /* 设置列和行的间隔 */
}

.grid-item {
  background: #f0f0f0;
  padding: 20px;
  border: 1px solid #ddd;
}

二十六、横线命名字符串转为驼峰命名字符串

js 复制代码
// 输入:str = "zhang-wsd-sd-s"
// 输出:zhangWsdSdS
function parseFn(str) {
  // 按照-把字符串截取成数组
  let arr = str.split("-");
  // 数组从下标1开始首字母转化成大写
  for (let i = 1; i < arr.length; i++) {
    arr[i] = arr[i].slice(0, 1).toUpperCase() + arr[i].slice(1);
  }
  // 拼接字符串
  return arr.join("");
}
parseFn("zhang-wsd-sd-s");

二十七、合并名称

js 复制代码
// 合并名称
// 书写一个函数,接受一个数组作为参数,数组内部由各个对象组成,
// 每个对象有age和name属性,将age一致的对象合并到一起,并且返回
// 输入: [ { age:1, name:"网" },{ age:2, name:"双" },{ age:1, name:"撒" }, { age:2, name:"旦" } ]
// 输出: [ { age:1,name: ["网", "撒"] }, { age:2,name: ["双", "旦"] }]

function fn(arr) {
  let obj = {};
  for (let i = 0; i < arr.length; i++) {
    if (obj.hasOwnProperty(arr[i].age)) {
      if (Array.isArray(obj[arr[i].age])) {
        obj[arr[i].age] = [...obj[arr[i].age], arr[i].name];
      } else {
        obj[arr[i].age] = [obj[arr[i].age], arr[i].name];
      }
    } else {
      obj[arr[i].age] = arr[i].name;
    }
  }
  let resArr = [];
  for (let i in obj) {
    resArr.push({ age: Number(i), name: obj[i] });
  }
  return resArr;
}
fn(arr);
相关推荐
爱吃鱼的锅包肉几秒前
Flutter路由模块化管理方案
前端·javascript·flutter
风清扬雨27 分钟前
Vue3具名插槽用法全解——从零到一的详细指南
前端·javascript·vue.js
大熊猫今天吃什么1 小时前
【一天一坑】空数组,使用 allMatch 默认返回true
前端·数据库
!win !1 小时前
Tailwind CSS一些你需要记住的原子类
前端·tailwindcss
前端极客探险家1 小时前
打造一个 AI 面试助手:输入岗位 + 技术栈 → 自动生成面试问题 + 标准答案 + 技术考点图谱
前端·人工智能·面试·职场和发展·vue
橘子味的冰淇淋~2 小时前
【解决】Vue + Vite + TS 配置路径别名成功仍爆红
前端·javascript·vue.js
利刃之灵2 小时前
03-HTML常见元素
前端·html
kidding7232 小时前
gitee新的仓库,Vscode创建新的分支详细步骤
前端·gitee·在仓库创建新的分支
听风吹等浪起2 小时前
基于html实现的课题随机点名
前端·html
leluckys2 小时前
flutter 专题 六十三 Flutter入门与实战作者:xiangzhihong8Fluter 应用调试
前端·javascript·flutter