# axios 对象创建过程

我们平常在使用的时候,会直接通过import axios from 'axios'引入axios对象,然后使用。那么这个axios对象到底是怎么来的呢?

本章节将会讲述 axios 对象创建的详细过程

# 源码分析

我们先来分析一下源码,源码是在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");

// 创建一个axios实例
function createInstance(defaultConfig) {
  // 通过`new`得到一个`Axios`实例,但是最终return的并不是这个实例
  var context = new Axios(defaultConfig);

  // 获取`Axios`原型链上面的`request`方法,并将其this绑定为context
  // 这里实际上可以写成`Axios.prototype.request.bind(context)`
  var instance = bind(Axios.prototype.request, context);

  // 遍历`Axios.prototype`原型上面的属性和方法,然后挂载到`instance`上面。如果是方法就需要将`this`绑定为`context`
  utils.extend(instance, Axios.prototype, context);

  // 循坏`context`实例上面的属性,并挂载到`instance`上面
  // 实际`context`实例上面就只有`defaults`和`interceptors`2个属性
  utils.extend(instance, context);

  return instance;
}

// 创建一个默认的axios对象,并将其导出
var axios = createInstance(defaults);

// 挂载`Axios`类
axios.Axios = Axios;

// 挂载`create`方法,用来创建新的axios对象
axios.create = function create(instanceConfig) {
  return createInstance(mergeConfig(axios.defaults, instanceConfig));
};

// 挂载取消请求的相关东西
axios.Cancel = require("./cancel/Cancel");
axios.CancelToken = require("./cancel/CancelToken");
axios.isCancel = require("./cancel/isCancel");

// 添加all和spread方法
axios.all = function all(promises) {
  return Promise.all(promises);
};
axios.spread = require("./helpers/spread");

// 导出axios
module.exports = axios;

module.exports.default = axios;

# 创建流程

我们把注意力集中在createInstance这个函数当中,axios对象的创建流程如下:

1、 通过new Axios得到一个context上下文实例

2、 获取Axios.prototype.request函数,并将函数的this绑定为context,得到一个instance函数

3、将Axios.prototype对象上面的属性和方法逐一挂载到instance函数上面,如果挂载的是一个函数,还需要把该函数的this绑定为context

4、将context实例上面的属性逐一挂载instance函数上面

5、调用createInstance函数创建一个默认的axios对象,并导出

# 其他

现在我们把注意力集中在 var axios = createInstance(defaults); 这行代码后面的代码当中。

我们可以发现,AxiosCancelCancelTokenisCancelcreateallspread这些类,函数,属性只有在默认导出的axios对象上面才存在的,通过axios.create创建的对象上面是不存在的。所以我们在使用这些函数或者属性的时候,要特别注意我们当前使用的是默认导出的对象还是使用axios.create创建的对象

# 注意点

细心的小伙伴可能会发现源码当中有这么 2 行代码:

utils.extend(instance, Axios.prototype, context);

utils.extend(instance, context);

其中 utils.extend(A,B) 函数是把B的属性或者方法挂在到A

有人可能会有疑问,context实例上已经存在了Axios.prototype上面的属性和方法,比方说Axios.prototype上面存在一个request方法,那么我们可以通过context.request去访问这个方法。为什么需要utils.extend(instance, Axios.prototype, context)这一行代码呢。

其实,在 js 中遍历一个对象的时候,是不能遍历到该对象所指向的prototype原型链上面的属性和方法,只能遍历到对象上本身的属性和方法。下面举个例子:

function People() {
  this.defaults = {};
  this.say = () => {};
}

People.prototype.sleep = function() {};

const demo = new People();

for (const key in axios) {
  console.log(key);
}

//  defaults,say

上面代码示例中输出的结果是defaults,say,虽然sleep方法可以通过demo.sleep去访问,但是却不能通过遍历去访问。所以才会需要以上 2 行代码,分别遍历挂载Axios.prototypecontext上面的属性和方法到instance函数上。

还有人可能会发现,utils.extend(instance, Axios.prototype, context)是传入了三个参数,utils.extend(instance, context)指传了两个参数。

Axios.prototype上面存在了一些函数,我们在挂载这些函数到instance函数上面的时候,需要修改this的指向为context,所以传了第三个参数。但是context实例遍历的时候,只存在属性,不会存在函数(跟Axios构造函数的组成有关),属性是不需要修改this的指向,所以不需要传入第三个参数(就算传了也没关系)。关于Axios构造函数是如何组成的,我们将会在下一个章节中讲解到,了解到Axios构造函数的内部组成之后,你就会更加清晰为什么context实例再遍历的时候,只会存在属性了。

# 简单代码实现

经过上述的学习,我们应该了解到了axios对象的创建过程了,下面,我们根据自己的理解去现实一个简单的代码,来加深印象。

function Axios(defaultConfig) {
  this.defaultConfig = defaultConfig;
}

Axios.prototype.request = function(config = {}) {};
Axios.prototype.get = function(config = {}) {};

function createInstance(config) {
  const context = new Axios(config);
  // 创建请求函数
  const instance = Axios.prototype.request.bind(context);

  // 将Axios.prototype中的request,post等方法挂载到instance请求函数中
  Object.keys(Axios.prototype).forEach((key) => {
    instance[key] = Axios.prototype[key].bind(context);
  });
  // 将context中的defaultConfig挂载到instance请求函数中
  Object.keys(context).forEach((key) => {
    instance[key] = context[key];
  });
  return instance;
}

const axios = createInstance(defaults);

# 总结

  • axios对象实际上是通过Axios.prototype.request函数通过bind(context)创建出来的(并不是通过 new Axios创建出来的),只是this的指向发生了变化。

  • Axios构造函数上面存在的属性和方法,axios对象都有。

  • axios对象实际上就是Axios.prototype.request函数,只是添加一系列属性和方法在这个函数上面

在本章中,我们看见Axios构造函数,在下一个章节中,我们将会分析Axios构造函数的内部组成