携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情
- 本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
- 这是源码共读的第19期,axios 工具函数
前言
提到axios,相信大家会自然地联想到get、post、delete等等,那么用axios是如何判断各种不同的数据类型并针对其类型完成相应的请求呢?这时候工具函数就派上大用场了,接下来就一起来分析一下axios源码中那些比较常用的工具函数,学以致用并了解大神们的编程思维吧~
学前准备
- 老规矩,用一个新东西前养成先看使用说明的好习惯,源码的使用说明就是它们:README.md、CONTRIBUTING.md
- 一般readme就是教你怎么用,contributing就是教你怎么跑源码,先甩个截图,加深印象
- 咱们今天的调试任务就是运行它啦
下载源码并运行
github默认是1.x版本,这里选择的是v2.x下载
git clone https://github.com/axios/axios.git
cd axios
npm start
打开 http://localhost:3000/ 出来这样的画面就说明运行成功啦,然后就可以打开控制台,点source愉快地调试源码了~
工具函数
接下来就是重头戏工具函数了,首先从入口函数index.js找到工具函数,它在这儿~
然后逐一开撸啦!
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用于存放二进制数据类型
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]",
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源码的英文文档对工具函数的解析也很详细,进一步证明了源码学习没我们想象中的那么难~