axios源码->utils 工具函数

130 阅读5分钟

1. 工具函数

1.1. 源码地址:

github.com/axios/axios…

今天的主角是utils.js文件, 以下列出了文件中的工具函数:

1.2. kindOf类型判断

const {toString} = Object.prototype;
const {getPrototypeOf} = Object;

const kindOf = (cache => thing => {
    const str = toString.call(thing);
    return cache[str] || (cache[str] = str.slice(8, -1).toLowerCase());
})(Object.create(null));

const kindOfTest = (type) => {
  type = type.toLowerCase();
  return (thing) => kindOf(thing) === type
}

const typeOfTest = type => thing => typeof thing === type;

先看kindOf方法

const {toString} = Object.prototype;

const kindOf = (cache => thing => {
    const str = toString.call(thing);
    return cache[str] || (cache[str] = str.slice(8, -1).toLowerCase());
})(Object.create(null));

1.2.1. 解析:

  1. 立即执行的函数表达式
(cache => thing => { ... })(Object.create(null));

这是一个立即执行的函数表达式,接收一个参数 cache 并立即执行。执行时,把 Object.create(null) 作为 cache 传递进去。Object.create(null) 创建一个干净的对象(没有继承任何属性)。

  1. 返回一个函数
cache => thing => { ... }

这个函数接收一个参数 thing,然后返回一个内部函数,该内部函数用于获取 thing 的类型并使用 cache 缓存结果。

  1. 获取类型字符串
const str = toString.call(thing);

使用 Object.prototype.toString.call(thing) 获取 thing 的类型字符串。例如,对于数组,返回的是 "[object Array]";对于对象,返回的是 "[object Object]"

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

检查 cache 中是否已有 str 的缓存值。如果有,则直接返回缓存值;如果没有,则计算类型并存入缓存中。

  • str.slice(8, -1) 提取类型字符串中的类型部分。例如,"[object Array]" 变成 "Array"
  • .toLowerCase() 将类型转换为小写。
  • 避免了再次执行切片操作,提高了函数的执行效率,尤其是在频繁调用的情况下。

1.2.2. 涉及知识:

  1. toString toString() 方法是JavaScript中所有对象继承自 Object.prototype 的一个方法,它用于将对象转换成字符串表示。这个方法非常基础且灵活,在不同的上下文中会有不同的表现形式。
  • 对于原始值:
    • 数值:toString() 会将数值转换为字符串形式,例如 5..toString()(5).toString() 返回 "5"
    • 字符串:尽管字符串本身已经是文本,调用 toString() 不改变其值,例如 ("hello").toString() 返回 "hello"
    • 布尔值: true.toString() 返回 "true", false.toString() 返回 "false"
    • undefined 和 null: 尝试直接调用会报错,因为它们不是对象,没有 toString 方法。
  • 对于对象:
    • 普通对象: 默认情况下返回 [object Object],不包含对象的实际内容。
    • 数组: Array.prototype.toString 被重写了,它会将数组内的元素用逗号连接起来形成一个字符串,例如 ["a", "b", "c"].toString() 返回 "a,b,c"
    • 日期对象: Date.prototype.toString() 返回一个可读的日期时间字符串,如 "Mon Aug 03 2020 14:56:27 GMT+0800 (中国标准时间)"
  • 使用基数参数:

当你需要将数字转换为特定进制的字符串时,可以给 toString() 传递一个基数参数(2到36之间的一个整数),例如:

(10).toString(2); // "1010" (二进制)
(255).toString(16); // "ff" (十六进制)
  • 类型检测:

正如之前提到的, Object.prototype.toString.call() 方法被广泛用于精确地检测一个值的数据类型,因为它会返回一个表示该对象类型的字符串,如 [object Array] [object Function] 等。

  1. 立即执行函数
  • 隔离作用域: IIFE允许创建一个新的作用域,这意味着定义在IIFE内部的变量(在这个例子中是cache)不会污染全局命名空间。这对于避免变量名冲突和保持代码的模块化是非常重要的。
  • 初始化缓存: 通过传递Object.create(null)给IIFE,它创建了一个没有原型链(即其原型是null)的新对象作为缓存。这个空对象可以用来存储类型字符串与其简化后的类型名称之间的映射,而不会影响到任何其他对象或全局命名空间。这个操作只在kindOf函数定义时发生一次,确保了缓存是干净且独立的。
  • 性能优化: 通过使用缓存,kindOf函数能够避免重复的字符串处理操作。一旦某类型的字符串被转换并存储在cache中,后续对该类型的检查就可以直接从缓存中读取,而不需要再次执行切片操作,这大大提高了函数的执行效率,尤其是在频繁调用的情况下。

**

**

1.3. isBuffer 判断是否是 Buffer

// 先判断不是 `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 类,该类用来创建一个专门存放二进制数据的缓存区。详细可以看 官方文档更通俗易懂的解释

因为axios可以运行在浏览器和node环境中,所以内部会用到nodejs相关的知识。

1.4. isFormData 判断FormData

// `instanceof` 运算符用于检测构造函数的 `prototype` 属性是否出现在某个实例对象的原型链上

function isFormData(val) {
  return (typeof FormData !== 'undefined') && (val instanceof FormData);
}


// instanceof 用法

function C() {}
function D() {}

const c = new C()

c instanceof C // output: true   因为 Object.getPrototypeOf(c) === C.prototype

c instanceof Object // output: true   因为 Object.prototype.isPrototypeOf(c)

c instanceof D // output: false   因为 D.prototype 不在 c 的原型链上

1.5. isObject 判断对象

// 排除 `null`的情况
function isObject(val) {
  return val !== null && typeof val === 'object';
}

历史遗留原因,typeof null === 'object';所以要先排除null类型

1.6. isPlainObject 判断 纯对象

纯对象: 用{}new Object()创建的对象。

function isPlainObject(val) {
  if (Object.prototype.toString.call(val) !== '[object Object]') {
    return false;
  }

  var prototype = Object.getPrototypeOf(val);
  return prototype === null || prototype === Object.prototype;
}


// 例子1
const o = {name: 'jay}
isPlainObject(o) // true

// 例子2
const o = new Object()
o.name = 'jay'
isPlainObject(o)   // true

// 例子3
function C() {}
const c = new C()
isPlainObject(c);  // false

// 其实就是判断目标对象的原型是不是`null` 或 `Object.prototype`