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

136 阅读6分钟

一、前言


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

第一次写源码笔记,在语雀建了一篇笔记写了标题后迟迟写不出后续,师父建议开头可以先参考别人的笔记,所以我在 语雀讨论区 阅读了一些大佬的笔记后,定了一篇笔记的大概目录结构,终于迈出了这第一步~

废话不多说,开干哈哈哈~

好好学习,天天向上!!!


二、目标

  • ✅如何调试JS代码
  • ✅了解学习axios源码中常用的工具函数

三、项目准备

//项目克隆
git clone https://github.com/axios/axios.git

//进入目录(因为我是直接在vscode中进入axios目录的,所以省略了这一步。)
cd axios

//运行项目
npm start

浏览器打开: http://localhost:3000/ 

浏览器查看效果:

四、调试

【配置 auto-attach】:

按 ****ctrl + shift + p,打开输入 > auto attach 。默认是 智能(smart)

提前打好断点,按 F5 运行,即可进入调试模式。或者在 package.json 找到相应的 scripts。鼠标悬浮在相应的命令上,会出现 运行命令 和 调试命令 两个选项,选择 调试命令 即可进入调试模式。

五、工具函数

路径:axios\lib\utils.js

1、 getProtocol 获取协议的名称

/**
* Returns URL protocol passed as param if is not undefined or null,
* otherwise just returns 'http:'
* 
* @param {String} protocol The String value of URL protocol
* @returns {String} Protocol if the value is not undefined or null
*/
function getProtocol(protocol) {
	return protocol || 'http:';
}

2、isArray 判断是否为数组

/**
 * Determine if a value is an Array
 * 判断是否为数组,一般返回Boolean类型的函数,命名都以‘is’开头
 * @param {Object} val The value to test
 * @returns {boolean} True if value is an Array, otherwise false
 */
function isArray(val) {
  return Array.isArray(val);
}

3、isUndefined 判断是否为undefined

/**
 * Determine if a value is undefined
 * 判断是否为undefined
 * @param {Object} val The value to test
 * @returns {boolean} True if the value is undefined, otherwise false
 */
function isUndefined(val) {
	//直接用typeof判断,注意typeof null === 'object'
  return typeof val === 'undefined';
}

4、isBuffer 判断buffer(缓冲区)

/**
 * Determine if a value is a Buffer
 * 
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a Buffer, otherwise false
 */
function isBuffer(val) {
  return val !== null //先判断不是‘undefined’和‘null’
          && !isUndefined(val) 
          && val.constructor !== null 
          && !isUndefined(val.constructor)
				  //再判断‘val’存在构造函数,因为‘buffer’本身是一个类
          && typeof val.constructor.isBuffer === 'function'
					//最后通过自身的‘isBuffer’方法判断
          && val.constructor.isBuffer(val);
}

5、isArrayBuffer 判断是否为arrayBuffer对象

/**
 * Determine if a value is an ArrayBuffer
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is an ArrayBuffer, otherwise false
 */
function isArrayBuffer(val) {
  return toString.call(val) === '[object ArrayBuffer]';
}

对于 Object.prototype.toString() 方法,会返回一个形如 "[object XXX]" 的字符串。如果对象的 toString() 方法未被重写,就会返回"undefined control sequence \h"。大多数对象,toString() 方法都是重写了的,这时,需要用 call() 或 Reflect.apply() 等方法来调用。

var name = {
  toString() {
    return "豆豆";
  },
};

name.toString();                                     // => "豆豆"

Object.prototype.toString.call(name);                // => "[object Object]"

Reflect.apply(Object.prototype.toString, name, []);  // => "[object Object]"

6、isFormData 判断FormData

/**
 * Determine if a value is a FormData
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is an FormData, otherwise false
 */
function isFormData(val) {
  return toString.call(val) === '[object FormData]';
	//or
	// `instanceof` 运算符用于检测构造函数的 `prototype` 属性是否出现在某个实例对象的原型链上
	return (typeof FormData !== 'undefined') && (val instanceof FormData);
}

7、isString 判断一个值是否是字符串对象或者字符串值

/**
 * Determine if a value is a String
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a String, otherwise false
 */
function isString(val) {
  const type = typeof value
  return type == 'string' || (type == 'object' && value != null && !Array.isArray(value) && getTag(value) == '[object String]')
}

8、 isObject 判断对象

/**
 * Determine if a value is an Object
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is an Object, otherwise false
 */
function isObject(val) {
  return val !== null && typeof val === 'object';
}

9、isPlainObject 判断 纯对象

/**
 * Determine if a value is an Object
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is an Object, otherwise false
 */
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`

10、isDate 判断Date

/**
 * Determine if a value is a Date
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a Date, otherwise false
 */
function isDate(val) {
  return toString.call(val) === '[object Date]';
}

11、isFile 判断文件类型

/**
 * Determine if a value is a File
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a File, otherwise false
 */
function isFile(val) {
  return toString.call(val) === '[object File]';
}

12、isBlob 判断Blob

/**
 * Determine if a value is a Blob
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a Blob, otherwise false
 */
function isBlob(val) {
  return toString.call(val) === '[object Blob]';
}

13、isFunction 判断函数

/**
 * Determine if a value is a Function
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a Function, otherwise false
 */
function isFunction(val) {
  return toString.call(val) === '[object Function]';
}

14、isStream 判断是否是流

/**
 * Determine if a value is a Stream
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a Stream, otherwise false
 */
function isStream(val) {
  return isObject(val) && isFunction(val.pipe);
}

15、isURLSearchParams 判断URLSearchParams

/**
 * Determine if a value is a URLSearchParams object
 * 构造器创建并返回一个新的URLSearchParams 对象。 开头的'?' 字符会被忽略。
 * @param {Object} val The value to test
 * @returns {boolean} True if value is a URLSearchParams object, otherwise false
 */
function isURLSearchParams(val) {
  return toString.call(val) === '[object URLSearchParams]';
}

16、trim 去除首尾空格

/**
 * Trim excess whitespace off the beginning and end of a string
 *
 * @param {String} str The String to trim
 * @returns {String} The String freed of excess whitespace
 */
function trim(str) {
  return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
}

17、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.
 * 如果是数组,回调将会调用value, index, 和整个数组
 *
 * If 'obj' is an Object callback will be called passing
 * the value, key, and complete object for each property.
 * 如果是对象,回调将会调用value, key, 和整个对象
 *
 * @param {Object|Array} obj The object to iterate
 * @param {Function} fn The callback to invoke for each item
 */
 
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') {
    obj = [obj];
  }

  if (isArray(obj)) {
    // Iterate over array values
    // 是数组,for循环执行回调fn
    for (var i = 0, l = obj.length; i < l; i++) {
      fn.call(null, obj[i], i, obj);
    }
  } else {
    // Iterate over object keys
    // 是对象,for循环执行回调fn
    for (var key in obj) {
       // 只遍历可枚举属性
      if (Object.prototype.hasOwnProperty.call(obj, key)) {
        fn.call(null, obj[key], key, obj);
      }
    }
  }
}

18、stripBOM删除UTF-8编码中BOM

所谓 BOM,全称是Byte Order Mark,它是一个Unicode字符,通常出现在文本的开头,用来标识字节序。UTF-8主要的优点是可以兼容ASCII,但如果使用BOM的话,这个好处就荡然无存了。

/**
 * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
 *
 * @param {string} content with BOM
 * @return {string} content value without BOM
 */
 
function stripBOM(content) {
  if (content.charCodeAt(0) === 0xFEFF) {
    content = content.slice(1);
  }
  return content;
}

六、收获与总结

看完工具库(utils.js)知道了各种类型的判断,forEach/trim 内部逻辑,了解了URLSearchParams/buffer。

以前写js都是用封装好的方法,今天看完源码发现好像也没有想象中那么晦涩难懂。一边看源码 一边看参考文章 一边百度,断断续续写了几小时,收益颇多。

七、相关参考

axios 工具函数

阅读axios源码,发现了这些实用的基础工具函数

新手向:前端程序员必学基本技能——调试JS代码

如何让 Vue、React 代码的调试变得更爽