【源码共读】axios 源码的经典面试题(一)

148 阅读2分钟

101181820-f3a63780-3612-11eb-9d3a-05452f2b0ad8.png

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 N 天,点击查看活动详情

本系列文章通过一步步阅读 axios 源码,同时发现并学习 / 回顾源码中涉及的关键知识点,仔细阅读本系列文章相信你会从中收获许多

一、源码文件目录:lib/axios.js

1.创建 axios 实例
function createInstance(defaultConfig) {
  var context = new Axios(defaultConfig);
  var instance = bind(Axios.prototype.request, context);

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

  utils.extend(instance, context);

  instance.create = function create(instanceConfig) {
    return createInstance(mergeConfig(defaultConfig, instanceConfig));
  };

  return instance;
}

思考一下,上面代码中涉及到哪些知识点?

没错,new 关键字和 bind 方法,那么你知道它们的作用和实现原理么?

new 关键字的作用:创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。

new 关键字会进行如下的操作:

  1. 创建一个空的简单 JavaScript 对象(即  {} );
  2. 为步骤 1 新创建的对象添加属性  __proto__ ,将该属性链接至构造函数的原型对象;
  3. 将步骤 1 新创建的对象作为 this 的上下文;
  4. 如果该函数没有返回对象,则返回 this
new 关键字的实现原理:
function customNew() {
  const obj = {};
  const Constructor = [].shift.call(arguments);
  Object.setPrototypeOf(obj, Constructor.prototype);
  const result = Constructor.apply(obj, arguments);

  return result instanceof Object ? result : obj;
};
bind 方法的作用:创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
bind 方法的实现原理:
Function.prototype.customBind = function (context, ...args) {
  if (!(this instanceof Function)) {
    throw new Error('函数才能调用 bind 方法!!!');
  }

  const _this = context || window;
  const fn = this;

  const Func = function () {};
  const resFn = function (...otherArgs) {
    const _resFn = this;
    const result = fn.apply(_resFn instanceof Func ? _resFn : _this, [...args, ...otherArgs]);

    return result;
  };

  Func.prototype = fn.prototype;
  resFn.prototype = new Func();

  return resFn;
};

call 方法的实现原理:
Function.prototype.customCall = function (context, ...args) {
  const _this = context || window;
  const fn = this;
  _this[fn.name] = fn;
  const result = _this[fn.name](args);
  delete _this[fn.name];

  return result;
};
apply 方法的实现原理:
Function.prototype.customApply = function (context, ...args) {
  const _this = context || window;
  const fn = this;
  _this[fn.name] = fn;
  // 与 call 唯一不同的是第二个参数
  const result = _this[fn.name](args[0]);
  delete _this[fn.name];

  return result;
};

导出 aixos 的方式

module.exports = axios;
module.exports.default = axios;

Get:这样写的好处是既支持命名导入,又支持默认导入。

二、源码文件目录:lib/core/Axios.js

请求方法挂载到 Axios 原型链上,可以不通过实例直接调用

Axios.prototype.request = function request(configOrUrl, config) {
   // 省略中间部分源码
    ... 
}

utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
  Axios.prototype[method] = function(url, config) {
     // 省略中间部分源码
     ...
  };
});

utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  // 省略中间部分源码
  ...
  
  Axios.prototype[method] = generateHTTPMethod();

  Axios.prototype[method + 'Form'] = generateHTTPMethod(true);
});