前言
在使用 Axios 时,我们通常会遇到两种调用方式:
// 方式一:直接传入配置对象
axios({
method: 'post',
url: '/user/12345',
data: { firstName: 'Fred' }
});
// 方式二:调用具体的方法
axios.get('/user?ID=12345');
axios.post('/user', { firstName: 'Fred' });
那么,axios 和 Axios 之间到底是什么关系呢 ?源码底层又是如何实现这两种调用方式的?
一、核心关系概述
在 Axios 源码中:
-
Axios:是一个构造函数(类),用于创建 Axios 实例。它的原型(prototype)上定义了 request、get、post、put、delete 等核心请求方法。

-
axios:是一个通过内部工厂函数创建出来的实例对象,本质上是一个函数(可以被调用),它指向了 Axios.prototype.request 方法,并且通过 bind 将 this 固定绑定了。
-
简单来说:axios 是 Axios 的一个实例,但它被设计成一个可直接调用的函数,同时还继承了原型上的所有方法。

二、源码关键行解析
1. 创建 Axios 实例(context)
// 第 21 行左右
var context = new Axios();
- 创建了一个 Axios 的实例对象,命名为 context。
- 这个 context 拥有 Axios.prototype 上定义的所有方法:request、get、post、put、delete 等。
2. 绑定 request 方法的 this 指向
// 第 25 行左右
var instance = bind(Axios.prototype.request, context);
- Axios.prototype.request 是一个综合请求方法,内部会访问 this.defaults(默认配置)和 this.interceptors(请求/响应拦截器),并最终发起请求。
- bind 函数将 request 方法的 this 永久绑定为 context(即 Axios 实例),确保 request 能正确读取配置和拦截器。
- 返回的函数赋值给 instance,也就是我们最终使用的 axios。
为什么需要绑定 this?
因为 request 方法内部有这样的逻辑(简化版):
// 伪代码
Axios.prototype.request = function(config) {
// 合并默认配置和用户配置
config = mergeConfig(this.defaults, config);
// 将拦截器和请求串联成链条
var chain = [dispatchRequest, undefined];
this.interceptors.request.forEach(…);
this.interceptors.response.forEach(…);
// … 执行链条
};
如果 this 指向全局对象(window/global),那么 this.defaults 和 this.interceptors 都会是 undefined,导致请求失败。因此必须通过 bind 将 this 固定指向 Axios 实例(context)。
3. 将原型方法拷贝到 instance 上
// 第 28 行左右
utils.extend(instance, Axios.prototype, context);
- utils.extend 方法将 Axios.prototype 上的所有属性和方法(get、post、put、delete 等)拷贝到 instance 对象上。
- 同时将方法的 this 绑定为 context,确保这些方法执行时的上下文正确。
这一步的作用
经过这一步,instance不仅是一个可以直接调用的函数,还拥有了 axios.get、axios.post 等快捷方法。
// 现在 instance 既可以这样用
instance({ method: 'get', url: '/user' });
// 也可以这样用
instance.get('/user');
instance.post('/user', { name: 'John' });
最后,将instance返回给axios,axios也就具备了这些能力。 
三、两种调用方式的底层原理
方式一:axios(config)
axios({
method: 'get',
url: '/user'
});
- 这里的 axios 就是经过 bind 处理后的 request 函数。
- 调用时,实际上执行的是 Axios.prototype.request,且内部的 this 指向 context(Axios 实例)。
- request 方法会根据 config.method 等参数,最终调用对应的请求方法。
方式二:axios.get(url, config)
axios.get('/user', { params: { id: 123 } });
- 这里的 axios.get 是从 Axios.prototype 上拷贝到 instance 上的方法。
- 调用时,它的 this 同样被绑定到了 context,因此可以正常访问实例上的其他属性和方法。
- 本质上,get 方法内部也是调用了 request 方法,只是帮我们预设了 method: 'get'。
四、设计意图总结
| Axios | 构造函数,核心请求逻辑都在它的原型上实现 |
| axios | 通过 bind 和 extend 创建出的“混合对象”,既是一个函数,又是一个拥有快捷方法的对象 |
| 为什么要这样做 | 为了让开发者可以使用两种风格来发起请求,提升灵活性和代码可读性 |
| this 绑定的必要性 | 确保 request 内部能够正确访问 defaults 和 interceptors 等实例属性 |
五、流程图解

六、核心结论
axios 实际上是一个被改造过的函数,它通过 bind 将 request 方法的 this 固定指向了 Axios 实例(context),并通过 extend 将原型上的请求方法拷贝到了自身。这样axios就同时支持了函数调用和链式方法调用两种风格。
- bind 的作用是绑定 this,确保 request 内部能正常访问 this.defaults 和 this.interceptors;
- extend 的作用是拷贝方法,让 axios 可以直接使用 .get、.post 等快捷方式;
- 两者结合,使 Axios 既保持了核心逻辑清晰,又提供了灵活的 API 设计。
七、补充:create 函数
源码中,默认导出的 axios 是通过内部工厂函数 createInstance(defaultConfig) 直接生成的。createInstance(defaultConfig)内部会执行上述 bind + extend 流程,并返回一个功能完整的实例。
而我们平时使用的 axios.create() 是暴露给用户的一个 API,是用赖创建一个新的、独立的 Axios 实例(拥有自己的 defaults 和 interceptors)。它的内部实现也是复用了同一个 createInstance 工厂函数。
因此,无论是默认的 axios 还是通过 axios.create() 创建的新实例,它们都遵循同样的构建机制,都支持两种调用方式。
希望这篇文章能帮助你理解 axios 和 Axios 之间的关系,以及源码中 bind 和 extend 的巧妙设计。如果你对源码感兴趣,建议可以去看看 Axios 的源代码(lib/axios.js 和 lib/core/Axios.js),会有更深的体会。 axios源码链接

