本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
学习准备
- 仓库地址: github.com/axios/axios
- 参考文章: 阅读axios源码,发现了这些实用的基础工具函数
- 线上vscode地址: github1s.com/axios/axios…
初始定义
// 定义一个表示该对象类型的字符串
const toString = Object.prototype.toString;
const kindOf = (cache => {
return thing => {
// 通过toString.call(thing)可以获取对象的类型
const str = toString.call(thing)
// 如果cache对象中存在str,则直接取cache[str] 的值,否则将str进行截取,转小写字符,将结果存在cache对象中,并返回结果
// 例如返回字符串:[object String],因此返回类型需要进行截取,从下标8到小标-1(左闭右开),负数代表倒数位置,最后转小写字符
return cache[str] || (cache[str] = str.slice(8, -1).toLowerCase());
};
})(Object.create(null)); // 是立即执行函数的形式,传入一个空对象,原型为null
// 判断是否是该类型
function kindOfTest(type) {
// 将此类型串转小写字母
type = type.toLowerCase();
return function isKindOf(thing) {
// 通过isKindOf传入thing进行判断该类型字符串是否是传入的类型字符串
return kindOf(thing) === type;
};
}
// 简易判断直接利用typeof,对object的判断不够准确
function typeOfTest(type) {
return thing => {
// 利用typeof判断类型
return typeof thing === type;
};
}
isArray 判断是否为数组
function isArray(val) {
return Array.isArray(val);
}
isUndefined 判断是否为undefined
const isUndefined = typeOfTest('undefined');
isBuffer
// 先判断不是 `undefined`和`null`
// 再判断 `val`存在构造函数,因为`Buffer`本身是一个类
// 最后通过自身的`isBuffer`方法判断
function isBuffer(val) {
return val !== null
&& !isUndefined(val)
&& val.constructor !== null
&& !isUndefined(val.constructor)
&& typeof val.constructor.isBuffer === 'function'
&& val.constructor.isBuffer(val);
}
什么是Buffer
JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。
但在处理像TCP流或文件流时,必须使用到二进制数据。因此在 Node.js中,定义了一个Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。详细可以看 官方文档 或 更通俗易懂的解释。
isString 判断是否为字符串类型
const isString = typeOfTest('string');
isFunction 判断是否为函数类型
const isFunction = typeOfTest('function');
isNumber 判断是否为数字类型
const isNumber = typeOfTest('number');
isObject 判断是否为对象类型
function isObject(thing) {
return thing !== null && typeof thing === 'object';
}
- 因为typeof null 的值是Object,所以判断是否是对象要排除null
isBoolean 判断是否为布尔类型
const isBoolean = thing => {
return thing === true || thing === false;
};
isPlainObject 判断是否为纯对象
function isPlainObject(val) {
if (kindOf(val) !== 'object') {
return false;
}
const prototype = Object.getPrototypeOf(val);
return prototype === null || prototype === Object.prototype;
}
- 纯对象: 用
{}或new Object()创建的对象。增加了判断目标对象的原型是不是null或Object.prototype
isDate 判断是否为日期类型
const isDate = kindOfTest('Date');
isFile 判断是否为文件类型
const isFile = kindOfTest('File');
isBlob 判断是否为Blob类型
const isBlob = kindOfTest('Blob');
Blob对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取。
isStream 判断是否是流
function isStream(val) {
return isObject(val) && isFunction(val.pipe);
}
isFormData 判断FormData
function isFormData(thing) {
const pattern = '[object FormData]';
return thing && (
// `instanceof` 运算符用于检测构造函数的 `prototype` 属性是否出现在某个实例对象的原型链上
(typeof FormData === 'function' && thing instanceof FormData) ||
toString.call(thing) === pattern ||
(isFunction(thing.toString) && thing.toString() === pattern)
);
}
isURLSearchParams 判断是否为url的查询字段
const isURLSearchParams = kindOfTest('URLSearchParams');
-
URLSearchParams接口定义了一些实用的方法来处理 URL 的查询字符串,可查看MDN地址:developer.mozilla.org/zh-CN/docs/… -
URL查询字符串形式:paramsString = "q=URLUtils.searchParams&topic=api"
trim 用于清除头尾的空格
function trim(str) {
return str.trim ? str.trim() : str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
}
isStandardBrowserEnv 判断是否是标准环境
function isStandardBrowserEnv() {
let product;
if (typeof navigator !== 'undefined' && (
(product = navigator.product) === 'ReactNative' ||
product === 'NativeScript' ||
product === 'NS')
) {
return false;
}
return typeof window !== 'undefined' && typeof document !== 'undefined';
}
forEach 遍历对象或数组
function forEach(obj, fn, {allOwnKeys = false} = {}) {
// 如果没有值直接返回
if (obj === null || typeof obj === 'undefined') {
return;
}
let i;
let l;
// 如果不是对象类型,强制转成数组类型
if (typeof obj !== 'object') {
/*eslint no-param-reassign:0*/
obj = [obj];
}
if (isArray(obj)) {
// 是数组执行for循环回调fn
for (i = 0, l = obj.length; i < l; i++) {
fn.call(null, obj[i], i, obj);
}
} else {
// 是对象,for循环执行回调fn,只遍历可枚举属性
const keys = allOwnKeys ? Object.getOwnPropertyNames(obj) : Object.keys(obj);
const len = keys.length;
let key;
for (i = 0; i < len; i++) {
key = keys[i];
fn.call(null, obj[key], key, obj);
}
}
}
endsWith 判断字符串是否以指定字符结尾
function endsWith(str, searchString, position) {
str = String(str);
if (position === undefined || position > str.length) {
position = str.length;
}
position -= searchString.length;
const lastIndex = str.indexOf(searchString, position);
return lastIndex !== -1 && lastIndex === position;
}
toArray 类数组转数组
function toArray(thing) {
if (!thing) return null;
if (isArray(thing)) return thing;
let i = thing.length;
if (!isNumber(i)) return null;
const arr = new Array(i);
while (i-- > 0) {
arr[i] = thing[i];
}
return arr;
}
toArray 是否是类数组
const isTypedArray = (TypedArray => {
// eslint-disable-next-line func-names
return thing => {
return TypedArray && thing instanceof TypedArray;
};
})(typeof Uint8Array !== 'undefined' && Object.getPrototypeOf(Uint8Array));
- TypedArray的MDN文档地址:developer.mozilla.org/zh-CN/docs/…
toCamelCase 转换为驼峰格式
const toCamelCase = str => {
return str.toLowerCase().replace(/[_-\s]([a-z\d])(\w*)/g,
function replacer(m, p1, p2) {
return p1.toUpperCase() + p2;
}
);
};
matchAll
function matchAll(regExp, str) {
let matches;
const arr = [];
while ((matches = regExp.exec(str)) !== null) {
arr.push(matches);
}
return arr;
}
- 包含所有匹配正则表达式和分组捕获结果的遍历器
- 因为返回的是遍历器,所以通常使用for...of循环取出。
总结
阅读源码的第二天,axios工具函数方法,有很多是可以在日常工作中直接使用,除了不常用的,其他的都比较常用,emmmm,很有道理。继续努力!!!