likes
comments
collection
share

【源码漫游】学习axios源码,详细解读基础工具函数

作者站长头像
站长
· 阅读数 49

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

hi, 我是小黄瓜没有刺。一枚菜鸟瓜🥒,期待关注➕ 点赞,共同成长~

本系列会精读一些小而美的开源作品或者框架的核心源码。

kindOf

用于判断一个值的类型 kindOf内部使用了闭包的特性将判断后的值进行缓存,避免对相同的参数多次执行

 // 定义toString方法
 var toString方法 = Object.prototype.toString;
 // 外层自执行函数,创建一个原型链指向null的对象当作参数传入
 var kindOf = (function(cache) {
   // 接收一个值
   return function(thing) {
     // 获取参数的类型,使用Object.prototype.toString方法
     // 返回值结构为 [object String]
     var str = toString.call(thing);
     // 首先查找cache对象中是否有值
     // 如果没有则对返回值进行截取转小写操作(slice(8, -1)指截取字符串的下标8开始到最后一个)
     // 将转换完成的字符串保存到对象中
     return cache[str] || (cache[str] = str.slice(8, -1).toLowerCase());
   };
 })(Object.create(null));

kindOfTest

用于生成一个判断是否是已存在类型的函数

 function kindOfTest(type) {
   // 将参数转为小些
   type = type.toLowerCase();
   // 返回一个函数,用于判断参数是否为指定类型
   return function isKindOf(thing) {
     // 利用kindOf函数转换并判断是否相等
     return kindOf(thing) === type;
   };
 }

整个工具函数库中有很多定义的类型参数:

isArrayBuffer

用于判断是否为ArrayBuffer类型

 var isArrayBuffer = kindOfTest('ArrayBuffer');

isDate

用于判断是否为Date类型

 var isDate = kindOfTest('Date');

isFile

用于判断是否为File类型

 var isFile = kindOfTest('File');

isBlob

用于判断是否为Blob类型

 var isBlob = kindOfTest('Blob');

isFileList

用于判断是否为FileList类型

 var isFileList = kindOfTest('FileList');

isURLSearchParams

用于判断是否为URLSearchParams类型

 var isURLSearchParams = kindOfTest('URLSearchParams');

isHTMLForm

用于判断是否为HTMLFormElementle类型

 var isHTMLForm = kindOfTest('HTMLFormElement');

isArray

用于判断是否为数组

 function isArray(val) {
   // 使用Array.isArray进行判断
   return Array.isArray(val);
 }

isString

用于判断是否为字符串类型

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

isNumber

用于判断是否为number类型

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

isObject

用于判断是否为对象类型,此处的对象包含数组,对象,时间对象等

 function isObject(val) {
   return val !== null && typeof val === 'object';
 }

isUndefined

判断是否为undefined

 function isUndefined(val) {
   return typeof val === 'undefined';
 }

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) {
   // toString为上文的Object.prototype.toString
   return toString.call(val) === '[object Function]';
 }

isBuffer

用于判断是否为Buffer类型

  • 先判断不是 undefinednull
  • 再判断是否存在构造函数,因为Buffer本身是一个类
  • 最后通过Buffer自带的的isBuffer方法判断
 /**
  * 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 && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor)
     && typeof val.constructor.isBuffer === 'function' && val.constructor.isBuffer(val);
 }

扩展

Node.js中,定义了一个Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。

Buffer 类是随 Node 内核一起发布的核心库。Buffer 库可以存储原始数据,让Node.js 处理二进制数据; 每当需要在 Node.js 中处理I/O操作中移动的数据时,就有可能使用 Buffer 库。 Buffer类是以字节数组的方式进行存储数据。

基本操作

Buffer.alloc(size[, fill[, encoding]]):返回一个指定大小的 Buffer 实例,如果没有设置 fill,则默认填满 0。

Buffer.from(array):使用0-255范围内的字节数组array来分配一个新的Buffer。如果array不是一个Array或适用于Buffer.from()变量的其他类型,则抛出TypeError

 // 从字符串创建一个buffer
 const buffer1 = Buffer.from('regis');
 console.log(buffer1);
 ​
 // 输出结果
 // <Buffer 72 65 67 69 73>
 // 从一个数组创建一个buffer
 const buffer2 = Buffer.from([1, 2, 3, 4]);
 console.log(buffer2);
 ​
 // 输出结果
 // <Buffer 01 02 03 04>
 // 创建一个长度20的空buffer
 const buffer3 = Buffer.alloc(20);
 console.log(buffer3);
 ​
 // 输出结果
 // <Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00>

判断Buffer

Buffer.isBuffer(obj):如果 obj 是一个 Buffer,则返回 true,否则返回 false

 const buf = Buffer.from('hello');
 ​
 console.log(Buffer.isBuffer(buf));//true
 console.log(Buffer.isBuffer({}));//false

isPlainObject

用于判断是否为对象直接创建的对象 例如直接使用{}new Object()创建的对象

 /**
  * Determine if a value is a plain Object
  *
  * @param {Object} val The value to test
  * @return {boolean} True if value is a plain Object, otherwise false
  */
 function isPlainObject(val) {
   // 首先确保val为对象类型
   if (kindOf(val) !== 'object') {
     return false;
   }
 ​
   // 获取对象的原型对象
   var prototype = Object.getPrototypeOf(val);
   // 判断是否为null 或者 Object.prototype
   // 如果直接使用字面量的方式,prototype 为 null
   // 使用 new Object() 来创建的对象,prototype 为 Object.prototype
   return prototype === null || prototype === Object.prototype;
 }
 let p1 = new Object()
 isPlainObject(p1) // true
 ​
 let p2 = { name: "小黄瓜" }
 isPlainObject(p2) // true
 ​
 function Person() {}
 let p3 = new Person()
 // p2的__proto__(原型指针)指向Person.prototype
 isPlainObject(p3) // false

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) {
   // 是否为对象并且为val具有pipe属性且为函数
   return isObject(val) && isFunction(val.pipe);
 }

Stream 是一个node.js中的一个抽象接口,Node 中有很多对象实现了这个接口。 而pipe则是Stream中管道的概念。

isFormData

用于判断是否为FormData对象

判断是否满足以下几种情况之一

  • FormData为函数的前提下,通过查找thing的原型链上是否可以查找到FormData.prototype(instanceof通过原型链进行查找)
  • 直接使用Object.prototype.toString进行转换
  • 如果thing具有toString函数,则直接调用此函数进行转换
 /**
  * Determine if a value is a FormData
  *
  * @param {Object} thing The value to test
  * @returns {boolean} True if value is an FormData, otherwise false
  */
 function isFormData(thing) {
   // 定义FormData类型
   var pattern = '[object FormData]';
 ​
   // let isFormData = kindOfTest('FormData')
   // 首先判断值是否存在,如果满足一下条件则说明是FormData类型
   return thing && (
     (typeof FormData === 'function' && thing instanceof FormData) ||
     toString.call(thing) === pattern ||
     (isFunction(thing.toString) && thing.toString() === pattern)
   );
 }

扩展

instanceof 是用来判断左侧对象是否是右侧构造函数的实例化对象,或则说左侧对象能否通过其隐式原型 [[proto]]在原型链上一层层向上查找到右侧函数的原型对象,即函数原型对象出现在实例对象的原型链上就返回 true

 function test () {} 
 test instanceof Function // true
 ​
 [1,2] instanceof Array // true
 ​
 ​
 let p1 = '1'
 let p2 = 2
 let p3 = [1, 2]
 let p4 = { name: "小黄瓜" }
 ​
 // instanceof无法判断基本类型
 p1 instanceof String // false
 p2 instanceof Number // false
 p3 instanceof Array // true
 p4 instanceof Object // true

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) {
   // 判断str是否具有trim方法,如果有的直接使用trim方法,没有的话使用正则进行匹配替换为""
   return str.trim ? str.trim() : str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
 }

forEach

将函数作用于数组/对象中的每一个值(原生的forEach仅支持数组,此处增加了对对象的支持)

 /**
  * @param {Object|Array} obj The object to iterate
  * @param {Function} fn The callback to invoke for each item
  */
 function forEach(obj, fn) {
   // 判断如果obj为null或者undefined,直接返回
   if (obj === null || typeof obj === 'undefined') {
     return;
   }
   // 如果obj不为object类型,直接包装为数组再进行处理
   if (typeof obj !== 'object') {
     obj = [obj];
   }
 ​
   if (isArray(obj)) {
     // 如果obj为数组,则直接遍历执行fn函数
     for (var i = 0, l = obj.length; i < l; i++) {
       // 将item(值) index(下标) obj(原对象)当作参数传递
       fn.call(null, obj[i], i, obj);
     }
   } else {
     // 如果obj为对象,则使用for in进行遍历
     for (var key in obj) {
       // 判断值是否为对象自有属性
       if (Object.prototype.hasOwnProperty.call(obj, key)) {
         fn.call(null, obj[key], key, obj);
       }
     }
   }
 }

merge

合并多个对象

 /**
  * @param {Object} obj1 Object to merge
  * @returns {Object} Result of all merge properties
  */
 // merge接收多个对象值
 function merge(/* obj1, obj2, obj3, ... */) {
   var result = {};
   // 定义处理函数此函数主要负责对key和value值进行赋值操作
   function assignValue(val, key) {
     // 如果key和value都为对象,则递归调用merge函数进行处理
     if (isPlainObject(result[key]) && isPlainObject(val)) {
       result[key] = merge(result[key], val);
       // 如果value为对象,则再次调用merge函数将value里面的子对象进行合并
     } else if (isPlainObject(val)) {
       result[key] = merge({}, val);
       // 如果value为数组,直接使用key进行赋值操作
     } else if (isArray(val)) {
       result[key] = val.slice();
     } else {
       // 普通对象直接赋值
       result[key] = val;
     }
   }
 ​
   // 遍历参数列表 -> [obj1, obj2, obj3...]
   for (var i = 0, l = arguments.length; i < l; i++) {
     // 使用forEach处理每个对象
     forEach(arguments[i], assignValue);
   }
   return result;
 }
 let o1 = {name: '小黄瓜'}
 let o2 = {age:18}
 let o3 = {obj: {isFn: true}}
 ​
 merge(o1, o2, o3 ) // {name: '小黄瓜', age: 18, obj: {isFn: true}}

extend

用于将b对象中的属性和方法绑定到a对象上

 /**
  * Extends object a by mutably adding to it the properties of object b.
  *
  * @param {Object} a The object to be extended
  * @param {Object} b The object to copy properties from
  * @param {Object} thisArg The object to bind function to
  * @return {Object} The resulting value of object a
  */
 ​
 function extend(a, b, thisArg) {
   forEach(b, function assignValue(val, key) {
     // thisArg如果存在,并且b对象中的值为函数,则通过调用bind将this绑定至当前对象即a
     if (thisArg && typeof val === 'function') {
       a[key] = bind(val, thisArg);
     } else {
       // 其他情况直接在a对象中进行设置 key-value
       a[key] = val;
     }
   });
   return a;
 }

stripBOM

去掉字节顺序标记 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;
 }

inherits

将一个构造函数的原型方法继承到另一个构造函数中去

 /**
  * Inherit the prototype methods from one constructor into another
  * @param {function} constructor
  * @param {function} superConstructor
  * @param {object} [props]
  * @param {object} [descriptors]
  */
 ​
 function inherits(constructor, superConstructor, props, descriptors) {
   // 使用Object.create构造一个prototype为superConstructor.prototype的对象
   // 重写constructor的原型对象
   constructor.prototype = Object.create(superConstructor.prototype, descriptors);
   // 重新定义constructor.prototype的constructor,因为重写后constructor属性会丢失
   constructor.prototype.constructor = constructor;
   // 如果传入了props属性,则将props与constructor进行合并
   props && Object.assign(constructor.prototype, props);
 }

endsWith

判断一个字符串是否以指定字符串的字符结束

 /**
  * Determines whether a string ends with the characters of a specified string
  *
  * @param {String} str
  * @param {String} searchString
  * @param {Number} [position= 0]
  *
  * @returns {boolean}
  */
 // 第三个参数为指定结束位置
 function endsWith(str, searchString, position) {
   // 确保str为字符串
   str = String(str);
   // 如果没有指定结束位置,则使用字符串的最后一个位置
   if (position === undefined || position > str.length) {
     position = str.length;
   }
   // 减去需要匹配字符串的长度,保证起始位置肯定在需要匹配的字符串之前
   position -= searchString.length;
   // indexOf第二个参数为指定开始查找的位置
   var lastIndex = str.indexOf(searchString, position);
   return lastIndex !== -1 && lastIndex === position;
 }

扩展

indexOf的第二个参数

indexOf传入第二个参数时指从那个位置开始进行查找

 var lastIndex = 'hello world'.indexOf('he', 3);
 console.log(lastIndex) // -1
 // 从几个位置开始查找
 ​
 var lastIndex = 'hello world'.indexOf('he');
 console.log(lastIndex) // 0
 ​
 var lastIndex = 'hello world'.indexOf('h', -3);
 console.log(lastIndex) // -1
 // 如果第二个参数为负数的情况
 // 从后往前取值 -> "rld" 然后从头开始查找下标

toArray

转换为数组

 /**
  * Returns new array from array like object or null if failed
  *
  * @param {*} [thing]
  *
  * @returns {?Array}
  */
 function toArray(thing) {
   // 判断参数不存在的情况,直接返回
   if (!thing) return null;
   // 判断参数已经为数组的情况,直接返回原数组
   if (isArray(thing)) return thing;
   var i = thing.length;
   // 判断长度的合法性
   if (!isNumber(i)) return null;
   // 创建数组,指定长度
   var arr = new Array(i);
   // 遍历赋值
   while (i-- > 0) {
     arr[i] = thing[i];
   }
   return arr;
 }

matchAll

遍历使用正则捕获字符串

 /**
  * It takes a regular expression and a string, and returns an array of all the matches
  *
  * @param {string} regExp - The regular expression to match against.
  * @param {string} str - The string to search.
  *
  * @returns {Array<boolean>}
  */
 function matchAll(regExp, str) {
   var matches;
   var arr = [];
   // 如果可以捕获到值,则继续进行捕获
   while ((matches = regExp.exec(str)) !== null) {
     arr.push(matches);
   }
   // 返回结果数组
   return arr;
 }

写在最后 ⛳

源码系列第四篇!未来可能会更新实现mini-vue3javascript基础知识系列,希望能一直坚持下去,期待多多点赞🤗🤗,一起进步!🥳🥳

转载自:https://juejin.cn/post/7110786105355698190
评论
请登录