jquery-ajax.js 说明文档

jquery-ajax.js 说明文档

概述

jquery-ajax.js 是一个完全独立 的 AJAX 封装库,API 与 jQuery 的 AJAX 方法100% 兼容。它只包含 AJAX 相关的功能,不包含 jQuery 的 DOM 操作、事件处理、动画等其他模块。

核心特点:

  • 📦 零依赖 --- 纯原生 JavaScript(基于 XMLHttpRequest),不需要引入 jQuery
  • 🔄 API 兼容 --- $.ajax() / $.get() / $.post() 等方法和 jQuery 用法完全一致
  • 💡 轻量 --- 仅约 13 KB(未压缩),约 460 行代码
  • Promise 支持 --- .done() / .fail() / .always() / .then() 链式调用
  • 🔧 全局配置 --- $.ajaxSetup() 统一管理默认参数
  • 🏷️ 命名空间保护 --- 内置 $.noConflict(),不污染 $ 符号

适用场景:

  • 不需要 jQuery,但习惯 jQuery AJAX 风格的开发者
  • 需要轻量级 AJAX 工具的项目
  • 不想引入整个 jQuery 但想用熟悉的 $.ajax() 语法的场景

快速开始

引入

html 复制代码
<script src="jquery-ajax.js"></script>

引入后,全局 $ 对象上即拥有所有 AJAX 方法。

第一个请求

javascript 复制代码
$.get('/api/users', function(data) {
  console.log('用户列表:', data);
});

API 参考

$.ajax(settings)

核心方法,所有其他快捷方法最终都调用它。

参数
参数 类型 默认值 说明
url String '' 请求地址
method / type String 'GET' HTTP 方法(GET / POST / PUT / DELETE / PATCH)
data Object / String null 发送到服务器的数据。对象会自动序列化为 query string
dataType String 'text' 期望的响应类型:text / json / xml / html / script
contentType String 'application/x-www-form-urlencoded; charset=UTF-8' 请求体的 Content-Type
async Boolean true 是否异步请求
timeout Number 0 超时时间(毫秒),0 表示不设超时
cache Boolean true GET 请求是否缓存。设为 false 会追加 _=时间戳 参数
processData Boolean true 是否将 data 对象自动转换为 query string
headers Object {} 自定义请求头键值对
beforeSend Function null 请求发送前的回调。接收 xhr 参数,返回 false 可取消请求
success Function null 请求成功时的回调。参数:data, status, xhr
error Function null 请求失败时的回调。参数:xhr, status, errorThrown
complete Function null 请求完成时的回调(无论成功或失败)。参数:xhr, status
返回值

返回一个类 Promise 对象,支持以下方法:

  • .done(function(data, status, xhr) {}) --- 成功时调用
  • .fail(function(xhr, status, errorThrown) {}) --- 失败时调用
  • .always(function() {}) --- 完成时始终调用
  • .then(doneFn, failFn) --- done + fail 的语法糖
  • .abort() --- 手动取消请求
基础示例
javascript 复制代码
// 完整配置示例
$.ajax({
  url: '/api/users',
  method: 'POST',
  data: {
    name: 'Alice',
    age: 28
  },
  dataType: 'json',
  timeout: 5000,
  headers: {
    'Authorization': 'Bearer token123'
  },
  beforeSend: function(xhr) {
    console.log('请求即将发送...');
    // return false;  // 取消请求
  },
  success: function(data, status, xhr) {
    console.log('成功:', data);
  },
  error: function(xhr, status, error) {
    console.error('失败:', status, error);
  },
  complete: function(xhr, status) {
    console.log('请求结束, 状态:', status);
  }
});
Promise 风格
javascript 复制代码
// 使用 .done / .fail / .always
$.ajax({
  url: '/api/config',
  dataType: 'json'
})
.done(function(data) {
  console.log('配置:', data);
})
.fail(function(xhr, status, err) {
  console.error('加载配置失败', err);
})
.always(function() {
  console.log('请求结束');
});

// 链式调用
$.ajax({ url: '/api/user/1', dataType: 'json' })
  .then(function(user) {
    // 第一个请求成功后,发起第二个请求
    return $.ajax({ url: '/api/orders?userId=' + user.id });
  })
  .done(function(orders) {
    console.log('订单:', orders);
  })
  .fail(function(err) {
    console.error('出错了', err);
  });
手动取消请求
javascript 复制代码
var req = $.ajax({
  url: '/api/long-task',
  success: function(data) {
    console.log('完成');
  }
});

// 2 秒后取消
setTimeout(function() {
  req.abort();
  console.log('请求已取消');
}, 2000);
发送 JSON 数据
javascript 复制代码
$.ajax({
  url: '/api/users',
  method: 'POST',
  contentType: 'application/json',
  processData: false,         // 不自动转 query string
  data: JSON.stringify({      // 手动序列化
    name: 'Alice',
    role: 'admin'
  }),
  dataType: 'json',
  success: function(data) {
    console.log('用户已创建:', data);
  }
});
文件上传(配合 FormData)
javascript 复制代码
var formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('category', 'image');

$.ajax({
  url: '/api/upload',
  method: 'POST',
  data: formData,
  contentType: false,   // 让浏览器自动设置 multipart/form-data
  processData: false,   // 不对 FormData 做序列化
  success: function(data) {
    console.log('上传成功:', data);
  }
});

$.get(url , data , success , dataType)

GET 请求快捷方法。

参数 类型 必填 说明
url String 请求地址
data Object / String 发送的数据(拼接到 URL 上)
success Function 成功回调 function(data, status, xhr)
dataType String 响应类型
javascript 复制代码
// 最简形式
$.get('/api/users');

// 带参数
$.get('/api/users', { page: 2, limit: 20 });

// 带回调
$.get('/api/users', function(data) {
  console.log(data);
});

// 带参数 + 回调
$.get('/api/users', { page: 2 }, function(data) {
  console.log(data);
});

// 指定响应类型
$.get('/api/users', { page: 2 }, function(data) {
  console.log(data);
}, 'json');

// Promise 风格(无 success 回调)
$.get('/api/users', { page: 1 })
  .done(function(data) { console.log(data); })
  .fail(function(xhr, status, err) { console.error(err); });

$.post(url , data , success , dataType)

POST 请求快捷方法。参数签名与 $.get() 完全一致。

javascript 复制代码
// 表单提交
$.post('/api/login', {
  username: 'admin',
  password: '123456'
}, function(response) {
  if (response.success) {
    console.log('登录成功');
  }
}, 'json');

// Promise 风格
$.post('/api/login', {
  username: 'admin',
  password: '123456'
})
.done(function(data) {
  console.log('登录成功', data);
})
.fail(function() {
  console.log('登录失败');
});

$.getJSON(url , data , success)

获取 JSON 数据的快捷方法,等价于 $.ajax({ dataType: 'json' })

javascript 复制代码
// 基础用法
$.getJSON('/api/config', function(config) {
  console.log('站点名称:', config.siteName);
});

// 带查询参数
$.getJSON('/api/search', { keyword: 'jQuery', limit: 10 }, function(results) {
  console.log('搜索结果:', results);
});

// Promise 风格
$.getJSON('/api/status')
  .done(function(data) { console.log('服务状态:', data); })
  .fail(function() { console.error('服务器不可用'); });

$.getScript(url , success)

动态加载并执行远程 JavaScript 文件。加载完成后可以立即使用文件中定义的变量和函数。

javascript 复制代码
// 加载插件
$.getScript('/js/chart-plugin.js', function() {
  console.log('图表插件加载完成');
  // 此时可以使用插件中的方法
  initChart();
});

// Promise 风格
$.getScript('/js/utils.js')
  .done(function() {
    console.log('工具库加载成功');
  })
  .fail(function() {
    console.error('加载失败');
  });

$.ajaxSetup(settings)

设置全局 AJAX 默认参数,对之后发出的所有请求生效。

javascript 复制代码
// 全局设置
$.ajaxSetup({
  timeout: 10000,          // 统一超时 10 秒
  dataType: 'json',        // 统一期望 JSON 响应
  cache: false,            // 统一禁用 GET 缓存
  headers: {
    'X-Custom-Header': 'my-app'
  },
  beforeSend: function(xhr) {
    // 自动附加 Token
    var token = localStorage.getItem('auth_token');
    if (token) {
      xhr.setRequestHeader('Authorization', 'Bearer ' + token);
    }
  },
  error: function(xhr) {
    // 统一的 401 处理
    if (xhr.status === 401) {
      window.location.href = '/login';
    }
  }
});

// 设置之后的请求自动沿用这些默认值
$.get('/api/users');           // 自动 JSON 解析、自动带 Token
$.post('/api/save', { ... });  // 同样沿用

注意: $.ajaxSetup() 影响全局,不建议在插件或公共库中使用,可能会干扰页面中其他代码的 AJAX 行为。


$.param(obj)

将 JavaScript 对象序列化为 URL 编码的参数字符串,等价于 jQuery 的 $.param()

复制代码
$.param(object) → string
javascript 复制代码
// 简单对象
$.param({ name: 'Alice', age: 28 });
// → "name=Alice&age=28"

// 嵌套对象
$.param({ user: { name: 'Alice', role: 'admin' } });
// → "user%5Bname%5D=Alice&user%5Brole%5D=admin"
// 解码后: "user[name]=Alice&user[role]=admin"

// 数组
$.param({ tags: ['js', 'html', 'css'] });
// → "tags%5B%5D=js&tags%5B%5D=html&tags%5B%5D=css"
// 解码后: "tags[]=js&tags[]=html&tags[]=css"

// 复杂嵌套
$.param({
  search: 'keyword',
  filters: { category: 'book', price: { min: 10, max: 50 } }
});
// → "search=keyword&filters%5Bcategory%5D=book&filters%5Bprice%5D%5Bmin%5D=10&filters%5Bprice%5D%5Bmax%5D=50"

$.noConflict(removeAll)

当页面中同时存在多个使用 $ 的库时,释放对 $ 的控制权。

参数 类型 默认值 说明
removeAll Boolean false 是否同时释放 jQuery 变量
javascript 复制代码
// 场景:页面先引入了本库,又引入了完整 jQuery
// <script src="jquery-ajax.js"></script>     ← 本库占用了 $
// <script src="jquery-3.7.1.min.js"></script> ← 完整 jQuery 也想要 $

var $ajax = $.noConflict();   // 释放 $ 还给 jQuery
// 之后:
//   $          → 完整 jQuery
//   $ajax      → 本库(AJAX 功能)
//   jQAjax     → 本库(备选命名空间,始终可用)

// 如果还想释放 jQuery 变量
var $ajax = $.noConflict(true);

与 jQuery AJAX 的 API 兼容对照表

功能 jQuery jquery-ajax.js 说明
核心 AJAX $.ajax() $.ajax() ✅ 完全兼容
GET 请求 $.get() $.get() ✅ 完全兼容
POST 请求 $.post() $.post() ✅ 完全兼容
JSON 请求 $.getJSON() $.getJSON() ✅ 完全兼容
动态加载 JS $.getScript() $.getScript() ✅ 完全兼容
全局配置 $.ajaxSetup() $.ajaxSetup() ✅ 完全兼容
参数序列化 $.param() $.param() ✅ 完全兼容
命名空间释放 $.noConflict() $.noConflict() ✅ 完全兼容
Promise (done/fail/always) 完全兼容
请求取消 .abort() .abort() 完全兼容
beforeSend 回调 完全兼容
JSONP dataType: 'jsonp' 未实现(现代项目使用 CORS)
全局 AJAX 事件 $(document).ajaxStart() 未实现(可通过 ajaxSetup 模拟)
statusCode 回调 statusCode: {404: fn} 未实现
converters $.ajaxSetup({ converters }) 未实现

结论: 日常开发中 95% 的 AJAX 场景完全兼容。


迁移指南

从 jQuery 迁移到 jquery-ajax.js

如果你的项目只使用了 jQuery 的 AJAX 功能,切换到本库只需两步:

  1. 替换 script 标签:
html 复制代码
<!-- 原来 -->
<script src="jquery-3.7.1.min.js"></script>

<!-- 改为 -->
<script src="jquery-ajax.js"></script>
  1. 代码无需修改 --- 零改动迁移: 所有 $.ajax / $.get / $.post / $.getJSON / $.getScript / $.ajaxSetup / $.param 调用保持不变。

与完整 jQuery 共存

如果页面中已经引入了完整 jQuery,且你也想使用本库的 AJAX 方法:

html 复制代码
<!-- 先引入完整 jQuery($ 被 jQuery 占用)-->
<script src="jquery-3.7.1.min.js"></script>
<!-- 再引入本库 -->
<script src="jquery-ajax.js"></script>

<script>
  // $ 仍然是完整 jQuery
  $('#myDiv').hide();       // jQuery DOM 操作

  // 用 jQAjax 访问本库的 AJAX 方法
  jQAjax.get('/api/data');
  jQAjax.ajax({ url: '/api/users' });

  // 或者立即释放 $ 让给 jQuery,用变量保存本库引用
  var $ajax = $.noConflict();
  // $ajax 就是本库
</script>

dataType 详解

dataType 响应处理方式 传给 success 的值
'text'(默认) 原样返回 字符串
'json' JSON.parse() 解析 JavaScript 对象 / 数组
'xml' 读取 xhr.responseXML XML Document
'html' 同 text HTML 字符串
'script' eval() 执行脚本内容 脚本文本

请求流程与回调执行顺序

复制代码
$.ajax() 被调用
  │
  ├─ 1. 合并 settings(默认 → 全局 → 当前)
  ├─ 2. 处理 data 序列化
  ├─ 3. 创建 XMLHttpRequest
  ├─ 4. 设置请求头
  │
  ├─ 5. beforeSend(xhr)          ← 可以在这里取消请求
  │
  ├─ 6. xhr.send()
  │
  ├─ 7. 等待响应...
  │
  ├─ 8. 响应到达:
  │     ├─ 成功 (2xx / 304):
  │     │    ├─ 按 dataType 转换数据
  │     │    ├─ success(data, 'success', xhr)
  │     │    └─ .done(data, 'success', xhr)
  │     │
  │     └─ 失败:
  │          ├─ error(xhr, 'error', errorThrown)
  │          └─ .fail(xhr, 'error', errorThrown)
  │
  └─ 9. complete(xhr, status)    ← 无论成功失败
       .always()

常见用法场景

场景一:RESTful API 调用

javascript 复制代码
// 查询列表
$.get('/api/articles', { page: 1, limit: 20 }, function(data) {
  renderList(data.items);
});

// 创建
$.ajax({
  url: '/api/articles',
  method: 'POST',
  contentType: 'application/json',
  data: JSON.stringify({ title: '新文章', content: '...' }),
  dataType: 'json',
  success: function(article) {
    console.log('已创建, ID:', article.id);
  }
});

// 更新
$.ajax({
  url: '/api/articles/42',
  method: 'PUT',
  contentType: 'application/json',
  data: JSON.stringify({ title: '修改后的标题' }),
  success: function() { console.log('已更新'); }
});

// 删除
$.ajax({
  url: '/api/articles/42',
  method: 'DELETE',
  success: function() { console.log('已删除'); }
});

场景二:统一错误处理 + Loading 指示器

javascript 复制代码
// 全局配置
$.ajaxSetup({
  timeout: 10000,
  beforeSend: function() {
    $('#loading').show();
  },
  complete: function() {
    $('#loading').hide();
  },
  error: function(xhr) {
    if (xhr.status === 401) {
      alert('登录已过期,请重新登录');
      window.location.href = '/login';
    } else if (xhr.status === 403) {
      alert('没有权限执行此操作');
    } else if (xhr.status >= 500) {
      alert('服务器错误,请稍后重试');
    }
  }
});

// 后续业务代码无需重复处理 Loading 和通用错误
$.get('/api/users', { page: 1 }, function(data) {
  renderTable(data);
});

场景三:并发请求等待

javascript 复制代码
// 同时发起多个请求,全部完成后再处理
var req1 = $.get('/api/users');
var req2 = $.get('/api/orders');
var req3 = $.get('/api/products');

var completed = 0;
var results = [];

function checkAll() {
  completed++;
  if (completed === 3) {
    console.log('三个请求全部完成:', results);
  }
}

req1.done(function(data) { results[0] = data; checkAll(); });
req2.done(function(data) { results[1] = data; checkAll(); });
req3.done(function(data) { results[2] = data; checkAll(); });

场景四:带重试的请求

javascript 复制代码
function fetchWithRetry(url, maxRetries) {
  maxRetries = maxRetries || 3;
  var attempts = 0;

  function attempt() {
    return $.ajax({ url: url, dataType: 'json' }).fail(function(xhr, status, err) {
      attempts++;
      if (attempts < maxRetries) {
        console.log('第 ' + attempts + ' 次重试...');
        return attempt();
      }
    });
  }

  return attempt();
}

// 使用
fetchWithRetry('/api/unreliable-endpoint', 3)
  .done(function(data) { console.log('成功:', data); })
  .fail(function() { console.error('重试 3 次后仍失败'); });

技术实现说明

方面 实现方式
底层传输 XMLHttpRequest(非 fetch),确保 beforeSend(xhr)xhr.abort() 可用
Promise 实现 自实现的 Deferred 模式(约 40 行),不依赖 ES6 Promise
参数序列化 自实现的 $.param(),支持嵌套对象和数组,与 jQuery 行为一致
命名空间 同时暴露 $ / jQuery / jQAjax 三个变量
模块封装 IIFE 模式,私有变量不污染全局

文件信息

复制代码
jquery-ajax.js          --- 主库文件(460 行,约 13 KB)
jquery-ajax-说明文档.md  --- 本说明文件

无需任何构建工具,直接在浏览器中通过 <script> 标签引入即可使用。

javascript 复制代码
/*!
 * jquery-ajax.js v1.0.0
 * 独立的 AJAX 封装库,API 与 jQuery AJAX 完全一致,不依赖 jQuery。
 * 适用于只想使用 $.ajax / $.get / $.post 等 AJAX 方法,而不想引入整个 jQuery 的场景。
 *
 * 包含方法:
 *   $.ajax()      - 核心 AJAX 方法
 *   $.get()        - GET 请求
 *   $.post()       - POST 请求
 *   $.getJSON()    - 获取 JSON 数据
 *   $.getScript()  - 动态加载并执行 JS 文件
 *   $.ajaxSetup()  - 全局默认设置
 *   $.param()      - 对象序列化为 URL 参数字符串
 *   $.noConflict() - 释放 $ 控制权
 *
 * 使用方式:
 *   <script src="jquery-ajax.js"></script>
 *   然后和 jQuery 一样使用 $ 即可(仅限 AJAX 相关方法)。
 */
;(function(global, undefined) {
  'use strict';

  // ==================== 内部工具 ====================

  // 获取值的类型字符串
  function type(obj) {
    if (obj === null) return 'null';
    if (obj === undefined) return 'undefined';
    var t = Object.prototype.toString.call(obj);
    return t.slice(8, -1).toLowerCase();
  }

  // 浅合并(后面的覆盖前面的)
  function extend(target) {
    var i = 1, len = arguments.length, src, key;
    for (; i < len; i++) {
      src = arguments[i];
      if (src != null) {
        for (key in src) {
          if (src.hasOwnProperty(key)) {
            target[key] = src[key];
          }
        }
      }
    }
    return target;
  }

  // 深度合并
  function extendDeep() {
    var target = arguments[0] || {}, i = 1, len = arguments.length,
        options, name, src, copy;
    for (; i < len; i++) {
      options = arguments[i];
      if (options != null) {
        for (name in options) {
          src = target[name];
          copy = options[name];
          if (copy !== undefined) {
            target[name] = copy;
          }
        }
      }
    }
    return target;
  }

  // 判断是否为纯对象(PlainObject)
  function isPlainObject(obj) {
    if (type(obj) !== 'object' || obj.nodeType || obj === global) {
      return false;
    }
    try {
      return obj.constructor &&
             Object.prototype.hasOwnProperty.call(obj.constructor.prototype, 'isPrototypeOf');
    } catch (e) {
      return false;
    }
  }

  // ==================== $.param() ====================

  function buildParams(prefix, obj, add) {
    if (type(obj) === 'array') {
      // 数组
      for (var i = 0, len = obj.length; i < len; i++) {
        var val = obj[i];
        if (type(val) === 'array' || isPlainObject(val)) {
          buildParams(prefix + '[' + i + ']', val, add);
        } else {
          add(prefix + '[]', val);
        }
      }
    } else if (isPlainObject(obj)) {
      // 对象
      for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
          var subVal = obj[key];
          var subPrefix = prefix ? prefix + '[' + key + ']' : key;
          if (type(subVal) === 'array' || isPlainObject(subVal)) {
            buildParams(subPrefix, subVal, add);
          } else {
            add(subPrefix, subVal);
          }
        }
      }
    } else {
      add(prefix, obj);
    }
  }

  function param(obj) {
    var parts = [];
    function add(key, value) {
      if (value === undefined || value === null) {
        value = '';
      }
      parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
    }
    if (type(obj) === 'array') {
      for (var i = 0, len = obj.length; i < len; i++) {
        var item = obj[i];
        if (item && item.name && item.value !== undefined) {
          // serializeArray 格式
          add(item.name, item.value);
        }
      }
    } else {
      buildParams('', obj, add);
    }
    return parts.join('&').replace(/%20/g, '+');
  }

  // ==================== Deferred / Promise ====================

  function createDeferred() {
    var _done = [], _fail = [], _always = [];
    var _state = 'pending';
    var _args_done, _args_fail;

    var promise = {
      done: function(cb) {
        if (_state === 'resolved') { cb.apply(null, _args_done); }
        else if (_state === 'pending') { _done.push(cb); }
        return this;
      },
      fail: function(cb) {
        if (_state === 'rejected') { cb.apply(null, _args_fail); }
        else if (_state === 'pending') { _fail.push(cb); }
        return this;
      },
      always: function(cb) {
        if (_state !== 'pending') { cb(); }
        else { _always.push(cb); }
        return this;
      },
      then: function(doneFn, failFn) {
        if (doneFn) this.done(doneFn);
        if (failFn) this.fail(failFn);
        return this;
      },
      state: function() { return _state; },
      promise: function() { return promise; }
    };

    var deferred = {
      resolve: function() {
        if (_state !== 'pending') return;
        _state = 'resolved';
        _args_done = Array.prototype.slice.call(arguments);
        for (var i = 0; i < _done.length; i++) _done[i].apply(null, _args_done);
        for (var j = 0; j < _always.length; j++) _always[j]();
      },
      reject: function() {
        if (_state !== 'pending') return;
        _state = 'rejected';
        _args_fail = Array.prototype.slice.call(arguments);
        for (var i = 0; i < _fail.length; i++) _fail[i].apply(null, _args_fail);
        for (var j = 0; j < _always.length; j++) _always[j]();
      },
      notify: function() {},
      promise: promise
    };

    return deferred;
  }

  // ==================== AJAX 核心 ====================

  // 默认配置
  var ajaxDefaults = {
    url: '',
    method: 'GET',
    type: 'GET',              // method 的别名
    data: null,               // 发送的数据(对象或字符串)
    dataType: 'text',         // 期望的响应类型: text, json, xml, html, script
    contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
    async: true,
    timeout: 0,
    cache: true,
    processData: true,        // 自动将 data 对象转换为 query string
    headers: {},
    beforeSend: null,         // function(xhr) --- return false 可取消请求
    success: null,            // function(data, status, xhr)
    error: null,              // function(xhr, status, errorThrown)
    complete: null            // function(xhr, status)
  };

  // 全局默认设置存储
  var globalSettings = {};

  /**
   * $.ajax(settings)
   * 核心 AJAX 方法,返回一个类 Promise 对象(支持 .done / .fail / .always / .then / .abort)。
   */
  function ajax(options) {
    var settings = extend({}, ajaxDefaults, globalSettings, options);

    // method / type 兼容
    settings.method = (settings.method || settings.type || 'GET').toUpperCase();

    // ---- 处理 data ----
    if (settings.processData && settings.data && typeof settings.data === 'object') {
      settings.data = param(settings.data);
    }

    // ---- 处理 cache(GET 请求禁用缓存时追加时间戳)----
    if (settings.method === 'GET' && settings.cache === false) {
      var sep = settings.url.indexOf('?') > -1 ? '&' : '?';
      settings.url += sep + '_=' + Date.now();
    }

    // ---- 处理 GET 请求:data 拼接到 URL ----
    if (settings.method === 'GET' && settings.data && typeof settings.data === 'string') {
      var sep2 = settings.url.indexOf('?') > -1 ? '&' : '?';
      settings.url += sep2 + settings.data;
      settings.data = null;
    }

    // ---- 创建 Deferred 和 XHR ----
    var deferred = createDeferred();
    var xhr = new XMLHttpRequest();

    xhr.open(settings.method, settings.url, settings.async);

    // 设置 Content-Type(GET 请求不需要)
    if (settings.contentType && settings.method !== 'GET') {
      xhr.setRequestHeader('Content-Type', settings.contentType);
    }

    // 自定义请求头
    for (var key in settings.headers) {
      if (settings.headers.hasOwnProperty(key)) {
        xhr.setRequestHeader(key, settings.headers[key]);
      }
    }

    // 超时
    if (settings.timeout > 0) {
      xhr.timeout = settings.timeout;
    }

    // ---- beforeSend ----
    if (typeof settings.beforeSend === 'function') {
      if (settings.beforeSend(xhr) === false) {
        // 取消请求
        deferred.reject(xhr, 'abort', 'Request cancelled by beforeSend');
        return deferred.promise;
      }
    }

    // ---- 响应处理 ----
    xhr.onreadystatechange = function() {
      if (xhr.readyState !== 4) return;

      var status = xhr.status;
      var responseText = xhr.responseText;
      var responseData = responseText;
      var isSuccess = (status >= 200 && status < 300) || status === 304 || status === 0;

      // 根据 dataType 转换响应数据
      if (isSuccess) {
        switch (settings.dataType) {
          case 'json':
            try {
              responseData = JSON.parse(responseText);
            } catch (e) {
              responseData = null;
              isSuccess = false;
            }
            break;
          case 'xml':
            responseData = xhr.responseXML;
            break;
          case 'script':
            try {
              global.eval.call(global, responseText);
            } catch (e) {
              // eval 失败
            }
            responseData = responseText;
            break;
          case 'html':
          case 'text':
          default:
            responseData = responseText;
            break;
        }
      }

      if (isSuccess) {
        // 成功
        if (typeof settings.success === 'function') {
          settings.success(responseData, 'success', xhr);
        }
        deferred.resolve(responseData, 'success', xhr);
      } else {
        // 失败
        var errorMsg = xhr.statusText || 'Unknown Error';
        if (typeof settings.error === 'function') {
          settings.error(xhr, 'error', errorMsg);
        }
        deferred.reject(xhr, 'error', errorMsg);
      }

      // complete 回调
      if (typeof settings.complete === 'function') {
        settings.complete(xhr, isSuccess ? 'success' : 'error');
      }
      // always 回调通过 deferred.always 触发(在 resolve/reject 中已处理)
    };

    // 发送请求
    var body = (settings.method === 'GET') ? null : (settings.data || null);
    xhr.send(body);

    // 附加 abort 方法到 promise
    deferred.promise.abort = function() {
      xhr.abort();
    };

    return deferred.promise;
  }

  // ==================== 快捷方法 ====================

  /**
   * $.get(url [, data] [, success] [, dataType])
   */
  function get(url, data, success, dataType) {
    if (typeof data === 'function') {
      dataType = dataType || success;
      success = data;
      data = undefined;
    }
    return ajax({
      url: url,
      method: 'GET',
      data: data,
      success: success,
      dataType: dataType
    });
  }

  /**
   * $.post(url [, data] [, success] [, dataType])
   */
  function post(url, data, success, dataType) {
    if (typeof data === 'function') {
      dataType = dataType || success;
      success = data;
      data = undefined;
    }
    return ajax({
      url: url,
      method: 'POST',
      data: data,
      success: success,
      dataType: dataType
    });
  }

  /**
   * $.getJSON(url [, data] [, success])
   */
  function getJSON(url, data, success) {
    if (typeof data === 'function') {
      success = data;
      data = undefined;
    }
    return ajax({
      url: url,
      method: 'GET',
      data: data,
      success: success,
      dataType: 'json'
    });
  }

  /**
   * $.getScript(url [, success])
   * 动态加载并执行 JavaScript 文件
   */
  function getScript(url, success) {
    return ajax({
      url: url,
      method: 'GET',
      dataType: 'script',
      cache: true,
      success: success
    });
  }

  // ==================== $.ajaxSetup() ====================

  /**
   * $.ajaxSetup(settings)
   * 设置全局 AJAX 默认参数,对之后所有请求生效。
   */
  function ajaxSetup(options) {
    extend(globalSettings, options);
  }

  // ==================== 构建 $ 命名空间 ====================

  var _$ = global.$;                    // 保存可能存在的旧 $
  var _jQuery = global.jQuery;          // 保存可能存在的旧 jQuery

  var $ = {
    ajax: ajax,
    get: get,
    post: post,
    getJSON: getJSON,
    getScript: getScript,
    ajaxSetup: ajaxSetup,
    param: param,

    // 用于获取当前活跃的 AJAX 请求数(简化版,始终返回 0,仅为 API 占位)
    active: 0,

    /**
     * $.noConflict(removeAll)
     * 释放 $ 控制权,返回当前库的引用。
     * removeAll: 是否同时释放 jQuery 变量
     */
    noConflict: function(removeAll) {
      global.$ = _$;
      if (removeAll) {
        global.jQuery = _jQuery;
      }
      return $;
    }
  };

  // 暴露到全局
  global.$ = $;
  global.jQuery = $;           // 同时暴露 jQuery 变量
  global.jQAjax = $;          // 备用命名空间,避免与完整 jQuery 冲突

})(window);
相关推荐
windliang2 小时前
Claude Code 源码分析(九):子 Agent 如何分叉、继续与回到父会话
前端·javascript·面试
柒和远方3 小时前
V063: TS 面试必考:interface 与 type 的四大差异,与 LLM Harness 的自动化择优
前端·javascript
半个落月3 小时前
React useRef 详解:DOM 引用、持久化值与 Worker 实例
前端·react.js
阿黎梨梨3 小时前
TypeScript 类型编程:从新手到 Harness 工程实践
前端
黄金决明子3 小时前
Vue3 + Vite 打包后打开空白?
前端
张龙6873 小时前
终端效率翻倍实战:fzf + zoxide + ripgrep + bat 组合拳,告别重复敲命令
前端
宿6743 小时前
vue3-env环境
前端·vue.js
蔬菜_3 小时前
前端转全栈-day5(数组、list、set)
java·前端·数据结构·list
汉堡大王95273 小时前
Vue 3 + TS + Element Plus 实战:如何从零搭建企业级违章记录管理 SaaS 前端
前端·javascript·vue.js