# 入口文件
首先从入口文件开始分析。
其文件路径为lib/axios.js
,源码如下:
var utils = require('./utils');
var bind = require('./helpers/bind');
var Axios = require('./core/Axios');
var mergeConfig = require('./core/mergeConfig');
var defaults = require('./defaults');
function createInstance(defaultConfig) {
// 调用Axios构造函数,生成实例
var context = new Axios(defaultConfig);
// 将Axios原型上的request方法的上下文绑定为context实例,并返回
// 现在instance就是Axios的原型方法request
// 直接调用instance()相当于request()
// 这就是Axios({})可以直接调用请求的原因
var instance = bind(Axios.prototype.request, context);
// 将Axios原型方法copy到instance函数上,例如get、post、delete等
// 如果原型方法是函数,则绑定上下文为context
utils.extend(instance, Axios.prototype, context);
// 将context的实例属性copy到instance方法上,主要为defaults和interceptors
utils.extend(instance, context);
// 返回绑定了上下文的『函数』
return instance;
}
// 创建一个包装函数,传入默认Config配置
var axios = createInstance(defaults);
axios.Axios = Axios;
// 工厂模式,通过create方法传入config生成一个Axios实例
axios.create = function create(instanceConfig) {
// 仍然是调用的createInstance函数,现在可以传入自定义配置来覆盖默认的配置
return createInstance(mergeConfig(axios.defaults, instanceConfig));
};
// 取消请求方法
axios.Cancel = require('./cancel/Cancel');
axios.CancelToken = require('./cancel/CancelToken');
axios.isCancel = require('./cancel/isCancel');
// 并发请求
axios.all = function all(promises) {
return Promise.all(promises);
};
axios.spread = require('./helpers/spread');
// 两种导出模式,因此可以通过以下两种方式导入
// import {axios} from 'axios'
// import axios from 'axios'
module.exports = axios;
module.exports.default = axios;
其做了如下功能
- 两种生成
axios
实例的方式,直接引用与create 工厂模式
- 将
axios.prototype.request
上下文绑定为axios
实例,返回了一个新『函数』 - 两种导出模式
all
、Cancel
等相关API
因此,这里我们就能够明白,为什么axios
不需要new
,还以函数的方式调用的原因。
但是为什么需要返回函数呢?下面举一个例子:
function bind(fn, thisArg) {
return function wrap() {
var args = new Array(arguments.length);
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i];
}
return fn.apply(thisArg, args);
};
};
// axios构造器
function axios(config) {
this.config = config;
}
axios.prototype.request = function () {
console.log('发送请求');
};
// 客户端调用
// 正常情况下,调用方式如下
// 1. 生成一个实例
const instance = new axios({});
// 2. 需要以『实例.方法』来调用
instance.request(); // output: 发送请求
// ************ 分隔线 *****************
// 如果将request原型方法的上下文绑定到当前实例上
// 现在的调用方式如下
// 1. 生成实例
// 2. 绑定其request方法在实例上
const instance = new axios({});
const instanceBind = bind(axios.prototype.request, instance);
// 3. 现在就可以instanceBind({})直接调用了
instanceBind(); // output: 发送请求
可以看出,最终返回的函数,并不是Axios
的实例,而是其原型方法request
。
通过这种方式,可以让调用请求语句更加优雅。
但是request
函数还缺少Axios
实例的属性
及原型方法
,因此还需要将这两部分复制到request
的原型上。
举例说明:
// ... 和上一段代码一致
// 在axios原型上添加一个方法
axios.prototype.get = function () {
console.log('get请求');
}
// instance(axios)实例原型上,的确存在get方法
console.log(instance.get); // [Function]
// instanceBind函数的原型上不存在get方法,因此我们得复制过来
// 因为Axios.prototype.request的原型上,的确不存在get方法
console.log(instanceBind.get); // undefined
# 总结
阅读了入口文件,我们能够得知。
客户端引入的axios
并不是类,所以不需要new
,它也不是直接返回的axios
实例。
其最终返回的是axios
的原型函数request
,此函数上下文指向实例,并复制了实例的属性和原型方法。
← 前言 默认Config配置 →