🔥 面试官手写 Promise 封装 AJAX这 5 䞪考点 90% 的人跪了

3 阅读2分钟

🔥 面试官手写 Promise 封装 AJAX这 5 䞪考点 90% 的人跪了

前蚀圚 AI 猖皋时代手写代码胜力还是前端工皋垈的"技城河"吗本文从䞀道真实面试题出发垊䜠圻底搞懂 AJAX、Fetch、Promise、深拷莝、内存管理 五倧栞心考点。文末附完敎代码暡板建议收藏


📋 目圕

1. 面试题匕入从 GitHub API 调甚诎起
2. AJAX vs Fetch本莚区别䞀匠囟看懂
3. 手写 getJSONPromise 封装 AJAX 完敎实现
4. 手写 sleep 凜数Promise 匂步控制粟髓
5. 深拷莝 vs 浅拷莝内存暡型终极解析
6. 面试高频考点总结 + 代码暡板

1⃣ 面试题匕入从 GitHub API 调甚诎起

先看䞀段"有问题"的代码䜠胜扟出几䞪 bug

<script>
    const members = document.querySelector('.member');
    const xhr = new XMLHttpRequest();
    xhr.open("GET", "https://api.github.com/orgs/lemoncode/members", true);
    xhr.send();
    xhr.onreadystatechange = function(){
        if(xhr.status === 200 && xhr.readyState === 4){
            const data = JSON.parse(xhr.responseText);
            console.log(data);
        }
    }
    // ❌ 臎呜错误圚回调倖访问匂步数据
    const data = JSON.parse(xhr.responseText);
    members.innerHTML = data.map(item => `<li>${item.login}</li>`).join("");
</script>

错误分析

序号问题䞥重性
1匂步时序错误回调倖访问 responseText🔎 臎呜
2选择噚䞍匹配.member vs members🔎 臎呜
3猺少错误倄理🟡 侭等
4回调地狱风险🟡 侭等

2⃣ AJAX vs Fetch本莚区别䞀匠囟看懂

┌─────────────────────────────────────────────────────────────┐
│                    AJAX vs Fetch 对比                        │
├──────────────────┬──────────────────┬────────────────────────
│      特性         │      AJAX        │       Fetch           │
├──────────────────┌──────────────────┌────────────────────────
│ 基于             │ XMLHttpRequest   │ Promise               │
│ 语法风栌         │ 回调凜数         │ then/catch/async-await│
│ 代码倍杂床       │ 高               │ 䜎                    │
│ 错误倄理         │ onerror 监听     │ catch 捕获            │
│ 默讀携垊 cookie  │ 是               │ 吊 (需 credentials)   │
│ 请求䞭止         │ abort()          │ AbortController       │
│ 浏览噚兌容性     │ IE8+             │ IE 䞍支持             │
└──────────────────┮──────────────────┮───────────────────────┘

代码对比

// ❌ AJAX 写法回调风栌
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.send();
xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
        if (xhr.status === 200) {
            const data = JSON.parse(xhr.responseText);
            // 嵌套回调...
        } else {
            // 错误倄理
        }
    }
};

// ✅ Fetch 写法Promise 风栌
fetch(url)
    .then(res => res.json())
    .then(data => {
        // 倄理数据
    })
    .catch(err => {
        // 错误倄理
    });

3⃣ 手写 getJSONPromise 封装 AJAX 完敎实现

面试考点

面试官请甚 Promise 封装䞀䞪 getJSON 凜数支持 GET 请求返回 JSON 数据。

完敎实现

/**
 * 手写 getJSON 凜数 - Promise 封装 AJAX
 * @param {string} url - 请求地址
 * @returns {Promise} - 返回 Promise 实䟋
 */
const getJSON = (url) => {
    return new Promise((resolve, reject) => {
        // 1. 创建 XMLHttpRequest 实䟋
        const xhr = new XMLHttpRequest();
        
        // 2. 初始化请求GET 方法匂步
        xhr.open('GET', url, true);
        
        // 3. 讟眮请求倎可选
        xhr.setRequestHeader('Content-Type', 'application/json');
        
        // 4. 发送请求
        xhr.send();
        
        // 5. 监听状态变化
        xhr.onreadystatechange = function() {
            // readyState 4 = 请求完成
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    try {
                        // 解析 JSON 数据
                        const data = JSON.parse(xhr.responseText);
                        // 成功调甚 resolve
                        resolve(data);
                    } catch (e) {
                        // JSON 解析倱莥
                        reject(new Error('JSON 解析倱莥: ' + e.message));
                    }
                } else {
                    // HTTP 状态码错误
                    reject(new Error(`请求倱莥状态码: ${xhr.status}`));
                }
            }
        };
        
        // 6. 监听眑络错误
        xhr.onerror = function() {
            reject(new Error('眑络错误'));
        };
        
        // 7. 监听超时可选
        xhr.ontimeout = function() {
            reject(new Error('请求超时'));
        };
    });
};

䜿甚瀺䟋

// 铟匏调甚
getJSON('https://api.github.com/orgs/lemoncode/members')
    .then(data => {
        console.log('成员列衚:', data);
        return data[0]; // 返回第䞀䞪成员
    })
    .then(firstMember => {
        console.log('第䞀䞪成员:', firstMember.login);
    })
    .catch(err => {
        console.error('错误:', err.message);
    })
    .finally(() => {
        console.log('请求完成无论成功倱莥');
    });

// async/await 写法曎䌘雅
async function fetchMembers() {
    try {
        const data = await getJSON('https://api.github.com/orgs/lemoncode/members');
        console.log(data);
    } catch (err) {
        console.error(err);
    } finally {
        console.log('完成');
    }
}

Promise 状态流蜬囟

                    ┌─────────────┐
                    │  PENDING    │
                    │  (等埅䞭)    │
                    └──────┬──────┘
                           │
            ┌──────────────┌──────────────┐
            │              │              │
            ▌              ▌              ▌
     ┌─────────────┐ ┌─────────────┐
     │ FULFILLED   │ │  REJECTED   │
     │   (成功)     │ │   (倱莥)     │
     │  then 执行   │ │  catch 执行  │
     └─────────────┘ └─────────────┘

4⃣ 手写 sleep 凜数Promise 匂步控制粟髓

面试考点

面试官劂䜕甚 Promise 实现䞀䞪 sleep 凜数让代码"暂停"指定时闎

完敎实现

/**
 * 手写 sleep 凜数
 * @param {number} ms - 䌑眠毫秒数
 * @returns {Promise} - 返回 Promise 实䟋
 */
function sleep(ms) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            // 时闎到状态从 pending 变䞺 fulfilled
            resolve(`䌑眠 ${ms}ms 完成`);
        }, ms);
    });
}

䜿甚瀺䟋

// 铟匏调甚
sleep(1000)
    .then(msg => {
        console.log(msg); // 1 秒后蟓出
        return sleep(2000);
    })
    .then(msg => {
        console.log(msg); // 再 2 秒后蟓出
    });

// async/await 写法掚荐
async function run() {
    console.log('匀始');
    await sleep(1000);
    console.log('1 秒后');
    await sleep(2000);
    console.log('3 秒后');
}
run();

⚠ 垞见错误

// ❌ 错误圚 setTimeout 䞭调甚 reject 䜆没有意义
function sleep(n) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(); // 这䌚让 Promise 变䞺倱莥状态
        }, n);
    });
}
// 结果䌚觊发 catch而䞍是 then

// ✅ 正确应该调甚 resolve
function sleep(n) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve(); // 成功状态
        }, n);
    });
}

5⃣ 深拷莝 vs 浅拷莝内存暡型终极解析

JS 内存暡型

┌─────────────────────────────────────────────────────────────┐
│                      JS 内存分垃                             │
├─────────────────────────┬────────────────────────────────────
│       栈内存 (Stack)     │          堆内存 (Heap)             │
├─────────────────────────┌────────────────────────────────────
│ • 简单数据类型           │ • 对象、数组、凜数                 │
│   (number, string,      │ • 䞍连续存傚                       │
│    boolean, null,       │ • 通过匕甚地址访问                 │
│    undefined, symbol)   │ • 垃土回收机制管理                 │
│ • 连续存傚访问快       │ • 存傚实际数据                     │
│ • 自劚分配释攟           │                                   │
└─────────────────────────┮───────────────────────────────────┘

浅拷莝 vs 深拷莝

const arr = [1, 2, { c: 3 }];

// 方法1concat (浅拷莝)
const arr1 = arr.concat([]);
arr1[2].c = 999;
console.log(arr[2].c); // 999 ❌ 原数组被圱响

// 方法2JSON (深拷莝有局限)
const arr2 = JSON.parse(JSON.stringify(arr));
arr2[2].c = 888;
console.log(arr[2].c); // 999 ✅ 原数组䞍受圱响

// 方法3structuredClone (现代浏览噚)
const arr3 = structuredClone(arr);

// 方法4递園实现 (完敎深拷莝)
function deepClone(target, hash = new WeakMap()) {
    if (target === null || typeof target !== 'object') return target;
    if (hash.has(target)) return hash.get(target);
    
    const clone = Array.isArray(target) ? [] : {};
    hash.set(target, clone);
    
    for (let key in target) {
        if (target.hasOwnProperty(key)) {
            clone[key] = deepClone(target[key], hash);
        }
    }
    return clone;
}

方法对比衚

方法埪环匕甚DateRegExpFunction兌容性
concat/展匀❌❌❌❌✅
JSON❌⚠⚠❌✅
structuredClone✅✅✅❌⚠
递園实现✅✅✅❌✅
lodash.cloneDeep✅✅✅❌✅

6⃣ 面试高频考点总结 + 代码暡板

📝 栞心考点枅单

┌─────────────────────────────────────────────────────────────┐
│                    前端匂步猖皋面试考点                       │
├──────────────────────────────────────────────────────────────
│ 1. AJAX 原理readyState、status、回调机制                   │
│ 2. Fetch APIPromise 基础、铟匏调甚、错误倄理               │
│ 3. Promise 手写状态流蜬、resolve/reject、then/catch       │
│ 4. async/await语法糖、错误倄理、并行/䞲行执行              │
│ 5. 深拷莝实现递園、埪环匕甚、特殊类型倄理                  │
│ 6. 内存暡型栈/堆区别、匕甚䌠递、垃土回收                   │
└─────────────────────────────────────────────────────────────┘

🎯 䞇胜代码暡板

// ============ 暡板1Promise 封装 AJAX ============
function getJSON(url, options = {}) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open(options.method || 'GET', url, true);
        xhr.timeout = options.timeout || 5000;
        
        xhr.onreadystatechange = () => {
            if (xhr.readyState === 4) {
                if (xhr.status >= 200 && xhr.status < 300) {
                    try {
                        resolve(JSON.parse(xhr.responseText));
                    } catch (e) {
                        reject(e);
                    }
                } else {
                    reject(new Error(`HTTP ${xhr.status}`));
                }
            }
        };
        
        xhr.onerror = () => reject(new Error('眑络错误'));
        xhr.ontimeout = () => reject(new Error('请求超时'));
        xhr.send();
    });
}

// ============ 暡板2sleep 凜数 ============
function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

// ============ 暡板3深拷莝凜数 ============
function deepClone(target, hash = new WeakMap()) {
    if (target === null || typeof target !== 'object') return target;
    if (target instanceof Date) return new Date(target);
    if (target instanceof RegExp) return new RegExp(target);
    if (hash.has(target)) return hash.get(target);
    
    const clone = Array.isArray(target) ? [] : {};
    hash.set(target, clone);
    
    for (let key in target) {
        if (target.hasOwnProperty(key)) {
            clone[key] = deepClone(target[key], hash);
        }
    }
    return clone;
}

📚 延䌞孊习

䞻题掚荐文章
Promise/A+ 规范Promises/A+ 官方规范
Event LoopJavaScript 事件埪环诊解
手写 Axios从零手写 Axios
匂步猖皋挔进从回调到 async/await

💬 互劚话题

䜠圚面试䞭遇到过哪些"手写代码"的坑

欢迎圚评论区分享䜠的经历点赞最高的送 前端面试题库 PDF 䞀仜


觉埗有甚请点赞 + 收藏 + 关泚䞋期预告《手写 Promise从 0 实现 Promises/A+ 规范》


本文参考了皀土掘金倚篇高赞文章结合 2025-2026 幎最新面试趋势敎理而成。劂有错误欢迎指正