刷刷题21(常见面试题)

1: 请求失败会弹出一个 toast , 如何保证批量请求失败, 只弹出一个 toast

方法一:使用全局标志位
javascript 复制代码
let isToastShown = false; // 全局标志位
 
function makeRequests() {
    const requests = [
        fetchPost(),
        fetchComments()
    ]; // 多个请求
 
    Promise.all(requests)
        .then(() => {
            // 所有请求成功的处理逻辑
        })
        .catch(errors => {
            if (!isToastShown) {
                // 检查标志位
                notify(errors[0]); // 弹出 toast
                isToastShown = true; // 设置标志位为 true
            }
        });
}
 
function fetchJSON(url, input) {
    return fetch(url, input)
        .then(res => {
            if (res.ok) {
                return res.json();
            }
            const err = new HttpError(res);
            if (!isToastShown) { // 检查标志位
                notify(err); // 弹出 toast
                isToastShown = true; // 设置标志位为 true
            }
            throw err; // 确保错误被抛出,否则后续逻辑可能无法捕获
        });
}
 
// 示例错误类
class HttpError extends Error {
    constructor(response) {
        super(`${response.status} ${response.statusText}`);
        this.name = "HttpError";
        this.response = response;
    }
}
 
// 重置标志位(在新的请求开始前)
makeRequests().finally(() => {
    isToastShown = false; // 确保在下一次请求时标志位被重置
});
方法二:使用防抖函数
ini 复制代码
// 防抖函数
function debounce(fn, delay = 1000) {
    let timeout;
    return function (...args) {
        clearTimeout(timeout);
        timeout = setTimeout(() => {
            fn.apply(this, args);
        }, delay);
    };
}
 
const notifyDebounced = debounce(message => {
    notify(message);
}, 3000); // 3 秒内只执行一次
 
// 示例调用
fetchJSON(url, input)
    .catch(err => {
        notifyDebounced(err); // 使用防抖后的通知函数
    });

2: js 如何判空? 「空」包含了:空数组、空对象、空字符串、0、undefined、null、空 map、空 set , 都属于为空的数据

javascript 复制代码
function isEmpty(value) {
  // 1. 基础空值判断
  if (value === null || value === undefined) return true;  // ‌:ml-citation{ref="1,3" data="citationList"}
  // 2. 字符串、数字判断
  if (typeof value === 'string' && value.trim() === '') return true;  // ‌:ml-citation{ref="3,5" data="citationList"}
  if (typeof value === 'number' && value === 0) return true;  // ‌:ml-citation{ref="4,5" data="citationList"}
  // 3. 数组判断
  if (Array.isArray(value) && value.length === 0) return true;  // ‌:ml-citation{ref="2,3" data="citationList"}
  // 4. 对象判断
  if (typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0) return true;  // ‌:ml-citation{ref="2,3" data="citationList"}
  // 5. Map/Set判断
  if ((value instanceof Map || value instanceof Set) && value.size === 0) return true;
  return false;
}
  1. ‌**typeof null 的陷阱**‌
    typeof null 返回 "object",需优先排除 null 干扰‌。

  2. ‌**0 的特殊性**‌

    若需区分 0 和"空值",需单独处理逻辑‌。

  3. 不可枚举属性的影响
    Object.keys() 忽略不可枚举属性,若需包含所有属性,改用 Object.getOwnPropertyNames()

  4. ES6+ 数据结构的兼容性

    Map/Set 的 size 属性需在支持 ES6 的环境中生效‌

    四、使用示例

scss 复制代码
isEmpty(null);              // true ‌
isEmpty(undefined);         // true ‌
isEmpty("   ");             // true ‌
isEmpty(0);                 // true ‌
isEmpty([]);                // true ‌
isEmpty({});                // true ‌
isEmpty(new Map());         // true
isEmpty(new Set());         // true

3:判断一个对象是否为空,包含了其原型链上是否有自定义数据或者方法。 该如何判定?

一、核心判断逻辑

  1. 遍历对象自身属性

    使用 Object.getOwnPropertyNames()Object.getOwnPropertySymbols() 获取对象自身的所有属性(含不可枚举和 Symbol 类型)‌

ini 复制代码
const ownProps = Object.getOwnPropertyNames(obj)
  .concat(Object.getOwnPropertySymbols(obj));

检查属性有效性

对每个属性检查其描述符(Descriptor),若存在非空值或函数,则对象非空‌

javascript 复制代码
for (const prop of ownProps) {
  const descriptor = Object.getOwnPropertyDescriptor(obj, prop);
  // 数据属性有值,或属性是函数,则非空
  if (descriptor.value !== null && descriptor.value !== undefined) return false;
  if (typeof descriptor.value === 'function') return false;
}

递归检查原型链

沿原型链向上遍历,重复上述步骤,直到 Object.prototype(排除内置原型的干扰)‌

javascript 复制代码
let proto = Object.getPrototypeOf(obj);
while (proto && proto !== Object.prototype) {
  const protoProps = Object.getOwnPropertyNames(proto)
    .concat(Object.getOwnPropertySymbols(proto));
  if (protoProps.length > 0) return false;
  proto = Object.getPrototypeOf(proto);
}

二、完整函数实现

javascript 复制代码
function isObjectTrulyEmpty(obj) {
  // 基础类型直接返回 true
  if (typeof obj !== 'object' || obj === null) return true;

  // 1. 检查自身属性
  const ownProps = Object.getOwnPropertyNames(obj)
    .concat(Object.getOwnPropertySymbols(obj));
  for (const prop of ownProps) {
    const descriptor = Object.getOwnPropertyDescriptor(obj, prop);
    if (descriptor.value !== null && descriptor.value !== undefined) return false;
    if (typeof descriptor.value === 'function') return false;
  }

  // 2. 检查原型链(排除内置原型)
  let proto = Object.getPrototypeOf(obj);
  while (proto && proto !== Object.prototype) {
    const protoProps = Object.getOwnPropertyNames(proto)
      .concat(Object.getOwnPropertySymbols(proto));
    if (protoProps.length > 0) return false;
    proto = Object.getPrototypeOf(proto);
  }

  return true;
}

三、使用示例

javascript 复制代码
// 空对象(自身和原型链无自定义属性)
console.log(isObjectTrulyEmpty({}));  // true

// 原型链有自定义方法
const objWithProto = Object.create({ method: () => {} });
console.log(isObjectTrulyEmpty(objWithProto));  // false

// 自身有不可枚举属性
const objWithHiddenProp = {};
Object.defineProperty(objWithHiddenProp, 'hidden', { value: 123 });
console.log(isObjectTrulyEmpty(objWithHiddenProp));  // false

4:浏览器有同源策略, 但是为何 cdn 请求资源的时候不会有跨域限制

一、同源策略的适用范围差异

  1. 限制目标不同

    同源策略的核心是防止恶意脚本窃取用户数据,主要限制以下行为:

    • 通过 XMLHttpRequestFetch API 发起的跨域请求(需 CORS 支持)‌12。
    • 跨域 DOM 操作(如通过 iframe 访问其他源的页面内容)‌1。
    • 跨域 Cookie 或 LocalStorage 操作‌1。

    静态资源(如图片、CSS、JS 文件) ‌ 的加载默认不受同源策略限制,浏览器允许跨域加载这些资源‌。

    资源敏感性差异

    CDN 分发的静态资源(如公开的 JS 库、图片)通常不包含敏感数据,浏览器认为此类资源对用户安全威胁较低,因此默认允许跨域加载‌

二、CDN 请求的特殊处理机制

  1. CORS 配置支持

    CDN 服务商通常会在服务器端配置 Access-Control-Allow-Origin 响应头,允许指定域或所有域(*)访问资源,从而绕过同源策略限制‌。例如:

makefile 复制代码
Access-Control-Allow-Origin: *

资源加载方式

HTML 标签(如 <script><img><link>)的 src 属性加载资源时,浏览器仅执行"读取"操作,不涉及跨域数据写入或敏感操作,因此不会触发同源策略拦截‌

三、CDN 的设计目标与实现

  1. 分发效率优先
    CDN 的核心目标是加速资源加载,若强制同源策略会阻碍跨域分发能力。因此 CDN 默认允许跨域请求,并通过 CORS 配置保障合法性‌46。
  2. 缓存与协议优化
    CDN 通过全局缓存节点分发资源,若跨域请求被限制,会降低缓存命中率和用户体验。浏览器对静态资源的宽松策略与 CDN 的优化目标一致‌

5:日志监控问题:可有办法将请求的调用源码地址包括代码行数也上报上去?

捕获调用堆栈

在发起请求时,通过 new Error().stack 获取当前调用堆栈信息,其中包含调用链中的文件路径和行号‌

arduino 复制代码
const stackTrace = new Error().stack;

解析堆栈信息

使用正则表达式从堆栈字符串中提取‌调用者文件路径 ‌和‌行号‌,例如:

ini 复制代码
const match = stackTrace.match(/at (.*):(\d+):(\d+)/);
const [filePath, line, column] = match.slice(1);

上报附加数据

将解析后的源码路径、行号、列号作为额外参数附加到请求日志中‌:

sql 复制代码
fetch(url, {
  headers: {
    'X-Source-Path': filePath,
    'X-Source-Line': line,
    'X-Source-Column': column
  }
});

二、关键实现步骤

  1. 封装统一请求方法

    拦截所有请求方法(如 fetchXMLHttpRequest),注入堆栈捕获逻辑:

javascript 复制代码
const originalFetch = window.fetch;
window.fetch = function(...args) {
  const stack = new Error().stack;
  // 解析并添加堆栈信息到请求头
  return originalFetch.apply(this, args);
};
  1. 兼容压缩代码场景

    • 开发环境‌:直接使用未压缩代码的堆栈信息‌1。
    • 生产环境 ‌:结合 ‌Source Map 文件 ‌,将压缩后的行号还原为源码位置(需借助 source-map 库)‌27。
  2. 性能优化

    • 按需采集‌:通过配置开关控制是否启用堆栈上报‌。
    • 抽样上报‌:对高频请求按比例抽样采集(如 10%)‌。

三、示例代码

ini 复制代码
// 拦截并增强 fetch 请求
const originalFetch = window.fetch;
window.fetch = async function(url, options) {
  // 捕获堆栈
  const stack = new Error().stack;
  const match = stack.match(/at (.*?):(\d+):(\d+)/);
  
  // 构造新请求头
  const newHeaders = new Headers(options?.headers);
  if (match) {
    newHeaders.set('X-Source-File', match‌:ml-citation{ref="1" data="citationList"});
    newHeaders.set('X-Source-Line', match‌:ml-citation{ref="6" data="citationList"});
  }

  // 发起请求
  return originalFetch(url, { ...options, headers: newHeaders });
};
相关推荐
图扑软件1 小时前
图扑农牧林数据分析可视化平台:智慧农业的“数字大脑”
javascript·人工智能·信息可视化·数据挖掘·数据分析·数字孪生·可视化
web150854159352 小时前
Spring Cloud Gateway
android·前端·后端
IT、木易2 小时前
大白话html第五章HTML5 新增表单元素和属性
前端·html·html5
一个处女座的程序猿O(∩_∩)O3 小时前
前端正则表达式完全指南:从入门到实战
前端·正则表达式
安静的小员5 小时前
IPv4应用场景API:精准识别IP属性,赋能业务决策
java·开发语言·前端·javascript·后端
贩卖纯净水.5 小时前
REACT学习--钩子剩余部分
前端·学习·react.js·前端框架
Ttang235 小时前
JavaWeb基础专项复习6——AJAX
xml·java·开发语言·前端·javascript·ajax
麗o麗6 小时前
C语言整体梳理-基础篇-预处理指令
java·c语言·前端
点点开心6 小时前
攻防世界WEB(新手模式)18-easyphp
前端·安全·web安全·网络安全