一、前言
本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
第一次写源码笔记,在语雀建了一篇笔记写了标题后迟迟写不出后续,师父建议开头可以先参考别人的笔记,所以我在 语雀讨论区 阅读了一些大佬的笔记后,定了一篇笔记的大概目录结构,终于迈出了这第一步~
废话不多说,开干哈哈哈~
好好学习,天天向上!!!
二、目标
- ✅如何调试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都是用封装好的方法,今天看完源码发现好像也没有想象中那么晦涩难懂。一边看源码 一边看参考文章 一边百度,断断续续写了几小时,收益颇多。