【源码学习】第④期 | 实用的axios 工具函数

183 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情

前言

提到axios,相信大家会自然地联想到get、post、delete等等,那么用axios是如何判断各种不同的数据类型并针对其类型完成相应的请求呢?这时候工具函数就派上大用场了,接下来就一起来分析一下axios源码中那些比较常用的工具函数,学以致用并了解大神们的编程思维吧~

学前准备

axios地址

  • 老规矩,用一个新东西前养成先看使用说明的好习惯,源码的使用说明就是它们:README.md、CONTRIBUTING.md
  • 一般readme就是教你怎么用,contributing就是教你怎么跑源码,先甩个截图,加深印象 图片.png
  • 咱们今天的调试任务就是运行它啦 图片.png

下载源码并运行

github默认是1.x版本,这里选择的是v2.x下载
git clone https://github.com/axios/axios.git
cd axios
npm start

打开 http://localhost:3000/ 出来这样的画面就说明运行成功啦,然后就可以打开控制台,点source愉快地调试源码了~

图片.png

工具函数

接下来就是重头戏工具函数了,首先从入口函数index.js找到工具函数,它在这儿~ 图片.png 图片.png


然后逐一开撸啦!

isArray 判断数组
function isArray(val) {

  return Array.isArray(val);

}

源码分析: 利用Array.isArray判断入参是否是数组


isUndefined 判断undefined
function isUndefined(val) {

  return typeof val === 'undefined';

}

源码分析:利用typeof判断值是否为undefined,typeof在两种情况下会返回undefined,①:变量没有被声明的时候 ②:变量等于undefined的时候


isBuffer 判断buffer
function isBuffer(val) {

  return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor)

    && typeof val.constructor.isBuffer === 'function' && val.constructor.isBuffer(val);

}

源码分析:判断入参非null或undefined后,利用buffer类构造器的isBuffer方法判断是否是buffer类型,buffer用于存放二进制数据类型

图片.png


isString 判断字符串
function isString(val) {

  return typeof val === 'string';

}

源码分析: 基本类型判断还得是typeof


isNumber 判断数字
function isNumber(val) {

  return typeof val === 'number';

}

源码分析: 跟字符串一样也是用的typeof判断


isObject 判断对象
function isObject(val) {

  return val !== null && typeof val === 'object';

}

源码分析:规避typeof判断null为object的bug来判断对象


isPlainObject 判断纯对象
var kindOf = (function(cache) {

  // eslint-disable-next-line func-names

  return function(thing) {

    var str = toString.call(thing);

    return cache[str] || (cache[str] = str.slice(8, -1).toLowerCase());

  };

})(Object.create(null));
function isPlainObject(val) {
  // Object.prototype.toString.call(val) 得到数据类型
  if (kindOf(val) !== 'object') {
    return false;
  }
  var prototype = Object.getPrototypeOf(val);
  return prototype === null || prototype === Object.prototype;
}

源码分析: 利用Object.prototype.toString得到"[object type]",其中type为数据类型,Object.getPrototypeOf() 方法返回指定对象的原型(内部[[Prototype]]属性的值,利用Object.create()创建null对象,Object.getPrototypeOf(val)为null


isFunction 判断方法
function isFunction(val) {

  return Object.prototype.toString.call(val) === '[object Function]';

}

源码分析: 用call方法调用Object.prototype.toString得到入参的"[object type]",

图片.png


isSteam 判断stream
function isStream(val) {
  return isObject(val) && isFunction(val.pipe);

}

源码分析:axios对http 服务器发起请求的request 对象就是一个 Stream,Stream是node.js的流类型


isFormData 判断formData
function isFormData(thing) {

  var pattern = '[object FormData]';

  return thing && (

    (typeof FormData === 'function' && thing instanceof FormData) ||

    toString.call(thing) === pattern ||

    (isFunction(thing.toString) && thing.toString() === pattern)

  );
}
const form = new FormData()
console.log(form,form instanceof FormData)
// expected output:[object FormData] true

源码分析:instanceof用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。instanceof用法:值 instanceof 构造器,感兴趣的也可以了解一下typeof跟instanceof的区别


trim 去首尾空格
function trim(str) {

  return str.trim ? str.trim() : str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');

}

源码分析: trim方法可以去首尾空格,不存在时利用正则替换


forEach 循环
function forEach(obj, fn) {

  // Don't bother if no value provided

  if (obj === null || typeof obj === 'undefined') {

    return;

  }

  // Force an array if not already something iterable

  if (typeof obj !== 'object') {

    /*eslint no-param-reassign:0*/

    obj = [obj];

  }

  if (isArray(obj)) {

    // Iterate over array values

    for (var i = 0, l = obj.length; i < l; i++) {

      fn.call(null, obj[i], i, obj);

    }

  } else {

    // Iterate over object keys

    for (var key in obj) {

      if (Object.prototype.hasOwnProperty.call(obj, key)) {

        fn.call(null, obj[key], key, obj);

      }

    }

  }

}

源码分析: 剔除null跟undefined特殊值后,若typeof检测数据类型不为object则设为数组类型,再分别根据对象、数组等执行循环


merge 合并多个对象
function merge(/* obj1, obj2, obj3, ... */) {

  var result = {};

  function assignValue(val, key) {

    if (isPlainObject(result[key]) && isPlainObject(val)) {

      result[key] = merge(result[key], val);

    } else if (isPlainObject(val)) {

      result[key] = merge({}, val);

    } else if (isArray(val)) {

      result[key] = val.slice();

    } else {

      result[key] = val;

    }

  }
  for (var i = 0, l = arguments.length; i < l; i++) {
    forEach(arguments[i], assignValue);

  }

  return result;

}

源码分析: 利用递归循环遍历对象属性并根据对象属性唯一性合并成新对象


extend 继承
function extend(a, b, thisArg) {

  forEach(b, function assignValue(val, key) {

    if (thisArg && typeof val === 'function') {
      a[key] = val.apply(thisArg);

    } else {

      a[key] = val;

    }

  });

  return a;

}

源码分析: 利用apply方法实现继承


endsWith 以**结尾
function endsWith(str, searchString, position) {

  str = String(str);

  if (position === undefined || position > str.length) {

    position = str.length;

  }

  position -= searchString.length;

  var lastIndex = str.indexOf(searchString, position);

  return lastIndex !== -1 && lastIndex === position;

}

源码分析: 用indexOf找到结尾字符串在目标字符串的索引位置


toArray 转换数组形式
function toArray(thing) {

  if (!thing) return null;

  if (isArray(thing)) return thing;

  var i = thing.length;

  if (!isNumber(i)) return null;

  var arr = new Array(i);

  while (i-- > 0) {

    arr[i] = thing[i];

  }

  return arr;

}

源码分析: 当传入值不为null、undefined、空值时,返回数组形式,当入参本身为数组时返回自身


总结

温故而知新,回顾了一下调试源码的流程,运行axios并对其中的工具函数进行了分析,进一步了解了axios对于不同数据类型的判断处理,axios源码的英文文档对工具函数的解析也很详细,进一步证明了源码学习没我们想象中的那么难~

参考文章

阅读axios源码,发现了这些实用的基础工具函数