# 入口文件

首先从入口文件开始分析。

其文件路径为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实例,返回了一个新『函数』
  • 两种导出模式
  • allCancel等相关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,此函数上下文指向实例,并复制了实例的属性和原型方法。

最近更新时间: 2023/3/21 19:40:56