内容概要:
- 源码寻宝的初衷
- 源码学习- VUE3 中的数据类型判断
- 总结
一、源码寻宝的初衷
1、源码-让我痛苦的天书
说实话第一次读VUE3.0源码的体验确实一点都不好, 之前听同事说读源码能了解框架的精髓,学习作者框架思想,提升的编程思路等,面试也会涉及源码。 抱着研究武功秘籍的心态,激动的打开源码,惊奇的发现里面几千个函数,层层调用,各种设计模式,然后我就蒙蔽了,别说看框架思路, 就连多进入几层函数调用之后,都会迷失,一团乱麻,找不到之前读到哪了, 源码虽然很重要,但带给我的是望而却步和遥不可及,痛苦和枯燥扑面而来。 不知道有没有同感的小伙伴,难道只有我是这样吗?
2、换个角度 柳暗花明
辗转反侧,框架博大精深,虽痛苦 但不能放弃,整体设计思想,各种牛X架构看不懂就看不懂吧,如果在框架中能学到一个零碎的小知识点也行,再大的框架在实现也会涉及到各种小细节和常用的基础功能, 比如 变量声明、 类型判断、错误处理,参数校验,分支逻辑处理等等,这些功能既常用又实用, 能学习到VUE大佬能如何实现这些基础功能,对后续的编码也会有很大帮助吧,抱着不贪多不放弃,学一点算一点的心态, 再看起源码来果然轻松多了。
3-不放弃-小收获:
抱着好奇的心态,在源码中东看西逛,还真发现有一些超级的实用的小收获,我想陆续分享给出来,一起交流进步!
二、源码学习- VUE3 中的数据类型判断
今天和大家一起来分享学习 VUE3 源码中几乎任何项目都必用的逻辑-- 判断数据类型, 来看看大佬们是如何来实现的吧!
1. 源码位置
- 使用命令:
npm i vue安装 Vue - 找到文件目录 node_modules/vue/dist/vue.global.js, 里面封装了大量的 vue 方法
- 位置:找到 343-366 行
const hasOwnProperty = Object.prototype.hasOwnProperty;
const hasOwn = (val, key) => hasOwnProperty.call(val, key);
const isArray = Array.isArray;
const isMap = (val) => toTypeString(val) === "[object Map]";
const isSet = (val) => toTypeString(val) === "[object Set]";
const isDate = (val) => toTypeString(val) === "[object Date]";
const isFunction = (val) => typeof val === "function";
const isString = (val) => typeof val === "string";
const isSymbol = (val) => typeof val === "symbol";
const isObject = (val) => val !== null && typeof val === "object";
const isPromise = (val) => {
return isObject(val) && isFunction(val.then) && isFunction(val.catch);
};
const objectToString = Object.prototype.toString;
const toTypeString = (value) => objectToString.call(value);
const toRawType = (value) => {
// extract "RawType" from strings like "[object RawType]"
return toTypeString(value).slice(8, -1);
};
const isPlainObject = (val) => toTypeString(val) === "[object Object]";
const isIntegerKey = (key) => isString(key) && key !== "NaN" && key[0] !== "-" && "" + parseInt(key, 10) === key;
上面的代码看起来是不是通俗易懂, 就算你是刚入门的小白, 也有兴趣读下去吧,简单又超级实用, 甚至可以直接拿到项目中使用, 这样看源码谁能不爱呢, 接下来一起来从源码中汲取营养吧!
2. 源码解读(个人观点,欢迎指正)
既然是大佬写的代码,一定有很多值得学习的地方,下面就依我小白的视角,分成片段来解读吧!
片段1:
const hasOwnProperty = Object.prototype.hasOwnProperty; // 检测属性是否为对象的自有属性
const objectToString = Object.prototype.toString;
const isArray = Array.isArray; // 判断是否为数组
上面三行代码,作者将对象的原型上的 hasOwnProperty()、toString()方法, 数组的静态方法 isArray(), 赋值给了对应的变量, 这么写看似有点多此一举,会有什么好处呢?
- 上面三个方法在Vue源码中频繁使用,赋值给变量后, 调用更方便, 代码更简洁, 不需要每次都书写繁杂的x.y.z...
- hasOwnProperty,objectToString, isArray 有很好的语义性, 提高源码的可读性
片段2:
const isFunction = (val) => typeof val === "function"; // 判断是否为函数
const isString = (val) => typeof val === "string"; // 判断是否为字符串
const isSymbol = (val) => typeof val === "symbol"; // 判断是否为symbol类型
上面三行代码,使用了typeof 来判断数据类型, 我记得 typeof 可以准确的判断基础数据类型(字符串、数字、布尔、symbol等),引用类型出来函数,其他的都返回object,所以代码实现了 函数、数字、symbol类型的判断,
- 但这里我有疑问了?大佬们为啥 没有用这种方式来 判断基础类型中的数字和布尔类型呢?经过了一系列的查缺补漏,我想是这样的吧:
- typeOf 判断 NaN 的结果也是nunber, 判断有效数字时,是有必要排除NaN的, 这里不妨看下源码中415行实现的toNumber()方法
// 处理数字时需要考虑NaN 的情况
const toNumber = (val) => {
const n = parseFloat(val); // 将传入的参数转成浮点型
return isNaN(n) ? val : n; // 参数非数字的情况下,会转成NaN,结果非数字直接返回原来的值,如果数字则返回数值
};
- 布尔类型为什么没有实现呢? 布尔数据本身就是用于判断条件的,直接交给if-else, for/while循环去判断不就好了,还要自己判断自己吗? 哈哈哈...
片段3:
const isObject = (val) => val !== null && typeof val === "object"; // 判断是否为对象
const isIntegerKey = (key) => isString(key) && key !== "NaN" && key[0] !== "-" && "" + parseInt(key, 10) === key; // 判断是否为字符串类型的数字
- typeof null 的结果也是object ,只是历史原因导致的, 判断对象时注意一下
- isIntegerKey() 是在判断key是否为十进制的正整数
- 知识点:parseInt()的第二个参数 表示进制
片段4:
const hasOwn = (val, key) => hasOwnProperty.call(val, key); // 判断val 自身是否用于 key属性
const toTypeString = (value) => objectToString.call(value); // 获取value的字符串类型 [object XXXX], XXXX就是类型
上面的代码,通过call函数调用, 将this执行call的第一个参数, 这样使传入的任何数据,都可以使用上面的两个功能了,666
片段5:
const isMap = (val) => toTypeString(val) === "[object Map]"; // 判断Map类型
const isSet = (val) => toTypeString(val) === "[object Set]"; // 判断Set类型
const isDate = (val) => toTypeString(val) === "[object Date]"; // 判断Date类型
const isPlainObject = (val) => toTypeString(val) === "[object Object]"; // 判断object类型
const toRawType = (value) => {
// extract "RawType" from strings like "[object RawType]"
return toTypeString(value).slice(8, -1);
};
上面的代码, 本质都是利用了Object.prototype.toString().call() 来实现的,toRawType()函数进行了再次封装,可以获取传入数据的类型字符串
片段6:
// 判断是否为Promise对象
const isPromise = (val) => {
return isObject(val) && isFunction(val.then) && isFunction(val.catch);
};
上面的函数判断是否为Promise类型,个人认为这是一个比较宽泛的判断,如果是一个对象,这个对象有then()、catch()方法,行了,这个的话 伪Promise对象也会被判断成功的,这样的会有更好的拓展性,不仅能支持原生的Promise,也可以支持符合Promise调用规范的其他对象哦
三、总结
到此为止,Vue3.0常用类型判断部分我们就解读完啦,这样看来源码是不是也没有那么难,并且还能学习到大佬们优秀的编程习惯和技巧,顺便也能收集点好用的工具函数库(HAHAHA,这是我最爱干的), 何乐不为呢,有兴趣的小伙伴们一起加入进来吧, 下一篇准备给大家分享 Vue源码中封装的超强大超好用的 判断任意类型相等的 looseEqual函数,敬请期待吧!