axios源码分析--基础工具函数

1.目标

学习axios源码中实用的工具函数

2.源码分析

2.1 isArray 判断数组

var toString = Object.prototype.toString;

 // 通过Object.prototype.toString来获取每个对象的类型
function isArray(val) {
  return toString.call(val) === '[object Array]';
}

2.2 isUndefined 判断Undefined

// 如果要判断是否是null,使用Object.prototype.toString
function isUndefined(val) {
  return typeof val === 'undefined';
}

2.3 isBuffer 判断Buffer

// a.判断val不是null和undefined
// b.因为Buffer本身是一个类,所以要判断它是否存在构造函数
// c.通过自身的isBuffer判断
// 在node中使用,Buffer.from('a')
function isBuffer(val) {
  return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor)
    && typeof val.constructor.isBuffer === 'function' && val.constructor.isBuffer(val);
}

2.4 isArrayBuffer 判断ArrayBuffer

var toString = Object.prototype.toString;
function isArrayBuffer(val) {
  return toString.call(val) === '[object ArrayBuffer]';
}

2.5 isFormData 判断FormData

// 先判断typeof FormData !== 'undefined',猜测原因是为了兼容IE,相关链接:https://*.com/questions/19486597/formdata-is-undefined-in-ie-only
function isFormData(val) {
  return (typeof FormData !== 'undefined') && (val instanceof FormData);
}

2.6 isArrayBufferView判断ArrayBufferView

function isArrayBufferView(val) {
  var result;
  // 此处主要是为了兼容IE9及以下
  if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) {
    result = ArrayBuffer.isView(val);
  } else {
    result = (val) && (val.buffer) && (val.buffer instanceof ArrayBuffer);
  }
  return result;
}

2.7 isString判断String

function isString(val) {
  return typeof val === 'string';
}

2.8 isNumber判断Number

function isNumber(val) {
  return typeof val === 'number';
}

2.9 isObject判断Object

// 看了下测试用例,数组也算对象,如果真实只检测object的话,还建议使用 Object.prototype.toString
function isObject(val) {
  return val !== null && typeof val === 'object';
}

2.10 isPlainObject判断PlainObject

// plainObject翻译为中文即为纯对象,所谓的纯对象,就是该对象是通过{}或new Object()创建的。
// 判断是否为“纯对象”,是为了和其他对象区分开比如说null、数组以及宿主对象(所有的DOM和BOM都是数组对象)等。
// 其实就是判断目标对象的原型是不是`null` 或 `Object.prototype
// jQuery中的isPlainObject() 函数用于判断指定参数是否是一个纯粹的对象,返回值为Boolean类型。
//“纯粹的对象”,就是通过 { }、new Object()、Object.create(null)  创建的对象。 
//这个方法的作用是为了跟其他的 JavaScript对象如 null,数组,宿主对象(documents),DOM 等作区分,因为这些用 typeof 都会返回object。
var toString = Object.prototype.toString;
function isPlainObject(val) {
  if (toString.call(val) !== '[object Object]') {
    return false;
  }
  // Object.getPrototypeOf() 方法返回指定对象的原型(内部[[Prototype]]属性的值)
  // Object.getPrototypeOf:实际上其实是获取当前对象的__proto__
  // Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
  var prototype = Object.getPrototypeOf(val);
  // Object.getPrototypeOf({}.__proto__)
  // Object.create(null)
  return prototype === null || prototype === Object.prototype;
}
// 测试用例
// expect(utils.isPlainObject({})).toEqual(true);
// expect(utils.isPlainObject([])).toEqual(false);
// expect(utils.isPlainObject(null)).toEqual(false);
// expect(utils.isPlainObject(Object.create({}))).toEqual(false);
// let b = {}
// expect(utils.isPlainObject(b).toEqual(true);

2.11 isDate判断Date

function isDate(val) {
  return toString.call(val) === '[object Date]';
}

2.12 isFile判断File

function isFile(val) {
  return toString.call(val) === '[object File]';
}

2.13 isBlob判断Blob

function isBlob(val) {
  return toString.call(val) === '[object Blob]';
}

2.14 isFunction判断Function

function isFunction(val) {
  return toString.call(val) === '[object Function]';
}

2.15 isStream判断Stream

function isStream(val) {
 // pipe是stream的一个方法,fs.createReadStream源码会使用到,
  return isObject(val) && isFunction(val.pipe);
}

2.16 isURLSearchParams判断URLSearchParams

// URLSearchParams 接口定义了一些实用的方法来处理 URL 的查询字符串
// var paramsString = "q=URLUtils.searchParams&topic=api"
//var searchParams = new URLSearchParams(paramsString);
//searchParams.has("topic") === true; // true
//searchParams.get("topic") === "api"; // true
//searchParams.getAll("topic"); // ["api"]
//searchParams.get("foo") === null; // true
//searchParams.append("topic", "webdev");
//searchParams.toString(); // "q=URLUtils.searchParams&topic=api&topic=webdev"
//searchParams.set("topic", "More webdev");
//searchParams.toString(); // "q=URLUtils.searchParams&topic=More+webdev"
//searchParams.delete("topic");
//searchParams.toString(); // "q=URLUtils.searchParams"
function isURLSearchParams(val) {
  return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams;
}

2.17 trim方法

function trim(str) {
  return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
}

2.18 判断是否事标准浏览器

/**
 * Determine if we're running in a standard browser environment
 *
 * This allows axios to run in a web worker, and react-native.
 * Both environments support XMLHttpRequest, but not fully standard globals.
 *
 * web workers:
 *  typeof window -> undefined
 *  typeof document -> undefined
 *
 * react-native:
 *  navigator.product -> 'ReactNative'
 * nativescript
 * nativescript介绍 https://zhuanlan.zhihu.com/p/29452684
 *  navigator.product -> 'NativeScript' or 'NS'
 */
function isStandardBrowserEnv() {
  if (typeof navigator !== 'undefined' && (navigator.product === 'ReactNative' ||
                                           navigator.product === 'NativeScript' ||
                                           navigator.product === 'NS')) {
    return false;
  }
  return (
    typeof window !== 'undefined' &&
    typeof document !== 'undefined'
  );
}

2.19 重写forEach方法

/**
 * Iterate over an Array or an Object invoking a function for each item.
 *
 * If `obj` is an Array callback will be called passing
 * the value, index, and complete array for each item.
 *
 * If 'obj' is an Object callback will be called passing
 * the value, key, and complete object for each property.
 *
 // 在迭代数组和对象的每一项时,执行传入的自定义方法
 // 如果obj是数组,callback执行参数为value, index, and complete array
 // 如果obj是对象,callback执行参数为value, key, and complete object
 // 此覆盖方法貌似只是比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);
      }
    }
  }
}

2.20 merge方法

/**
 * Accepts varargs expecting each argument to be an object, then
 * immutably merges the properties of each object and returns result.
 *
 * When multiple objects contain the same key the later object in
 * the arguments list will take precedence.
 *
 * Example:
 *
 * ```js
 * var result = merge({foo: 123}, {foo: 456});
 * console.log(result.foo); // outputs 456
 * ```
 *
 * @param {Object} obj1 Object to merge
 * @returns {Object} Result of all merge properties
 */
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;
}

2.21 extend方法

/**
 * Extends object a by mutably adding to it the properties of object b.
 */
function extend(a, b, thisArg) {
  forEach(b, function assignValue(val, key) {
    if (thisArg && typeof val === 'function') {
      a[key] = bind(val, thisArg);
    } else {
      a[key] = val;
    }
  });
  return a;
}

2.22 stripBOM方法

/**
 * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
 */
 // 确定json文件是UTF-8 无BOM编码的的。如果有BOM,会在读取第一行的时候出现乱码。
 // 所谓 BOM,全称是Byte Order Mark,它是一个Unicode字符,通常出现在文本的开头,用来标识字节序。UTF-8主要的优点是可以兼容ASCII,但如果使用BOM的话,这个好处就荡然无存了。
 // 删除UTF-8编码中BOM
function stripBOM(content) {
  if (content.charCodeAt(0) === 0xFEFF) {
    content = content.slice(1);
  }
  return content;
}

3.知识扩展

3.1 判断数据类型的几种方式

// 数组最好使用typeof判断,这样定义()=> {}的方法,会被Object.prototype.toString识别为Object
1.typeof:仅能判断基础数据类型,如:“number”,”string”,”undefined”,”boolean”,“symbol” (ES6新增)六种,”object”类型建议使用Object.prototype.toString判断
2.instanceof 用于判断一个变量是否某个对象的实例,如:Object Array Function
// 所有的均可使用下边方法判断
3.Object.prototype.toString:对于数组、null、对象来说,其关系错综复杂,使用 typeof 都会统一返回 “object” 字符串。所以用此方法来判断

3.2 Buffer相关知识

JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。
但在处理像TCP流或文件流时,必须使用到二进制数据。因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。
在 Node.js 中,Buffer 类是随 Node 内核一起发布的核心库。Buffer 库为 Node.js 带来了一种存储原始数据的方法,可以让 Node.js 处理二进制数据,每当需要在 Node.js 中处理I/O操作中移动的数据时,就有可能使用 Buffer 库。原始数据存储在 Buffer 类的实例中。一个 Buffer 类似于一个整数数组,但它对应于 V8 堆内存之外的一块原始内存。

3.3 NodeJS中ArrayBuffer与Buffer的异同

  • ArrayBuffer对象用来表示通用的、固定长度的原始二进制数据缓冲区,是一个字节数组,可读但不可直接写。
  • Buffer 是 Node.JS 中用于操作 ArrayBuffer 的视图,是 TypedArray的一种。

4.总结

附:

1.源码链接

上一篇:React 开发错误记录


下一篇:使用vite搭建vue3项目(六)