【若川视野 x 源码共读】第19期 | axios 工具函数

239 阅读4分钟

本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。

学习准备

初始定义

// 定义一个表示该对象类型的字符串
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()创建的对象。增加了判断目标对象的原型是不是nullObject.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));

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,很有道理。继续努力!!!