嘿,伙计们!下一代 Vue 已经发布。不仅有全新的合成 API,更强大和灵活的反应系统,一流的渲染功能,而且还有构建现代浏览器的自然性能。
关于 VUE3 和源代码分析的帖子和教程已经有上百篇。本系列是关于源代码阅读的,但包括相关的技术说明。如果是你的问题,请保持沉默。
作为本系列的第一篇文章,不妨细细品味一下 Vue 3 的一小部分。HasChanged
用于比较某个值是否发生了变化,以说明 NaN,并利用 trigger 函数避免不必要的影响函数重新运行,该函数位于vue/@shared
。源代码片段如下所示:
export const hasChanged = (value: any, oldValue: any): boolean => { return !Object.is(value, oldValue) }
这是多么简单。但Object.is
是什么?
Object.is 是什么?
Object.is
方法来自 ES6,能够确定两个值是否相同。如果满足以下条件之一,则两个值相同:
- 两个都是
undefined
- 两个都是
null
- 两个长度相同、字符顺序相同的
string
- 两个都是
true
或者都是false
- 两个
object
对象都引用堆中分配的相同内存地址 - 两个值是
number
:- 都是
-0
或者都是+0
- 都是
NaN
- 都是非零和非
NaN
都具有相同的值
- 都是
正如我们所知,相等运算符(==)在测试相等性之前,如果两侧的类型不同,则会对它们应用各种强制。但是Object.is
不强制任何一个值。
严格相等运算符(==
)和Object.is
之间的区别在于它们对有符号零和NaN
的处理。
NaN 是相等和严格相等的特殊值
NaN
代表非数字,两个NaN
值之间通过相等或严格相等进行比较将导致错误。我们可以通过调用x!==x
来确定值是否为NaN
。
然而,来自 ES5 的window.isNaN
有助于我们确定某个点上的值是否为NaN
类型。但有一种方式是我们可以忽略的重要而微妙的细节。这就是在比较之前进行类型转换,如下所示
isNaN(123) //false isNaN(-1.23) //false isNaN(5-2) //false isNaN(0) //false isNaN('123') //false isNaN('Hello') //true isNaN('2005/12/12') //true isNaN('') //false isNaN(true) //false isNaN(undefined) //true isNaN('NaN') //true isNaN(NaN) //true isNaN(0 / 0) //true isNaN(null) //false
因此,当我们想要比较一个值是否已经改变,将 NaN 解释为 Vue 3 时,我们应该编写如下代码:
function hasChanged(x, y) { x !== y && !(typeof x === 'number' && isNaN(x) && typeof y === 'number' && isNaN(y)) }
因为window.isNaN
会在比较之前首先将其参数强制为数字类型,所以我们必须采取一些措施来构建自己的严格 isNaN。幸运的是,ES6 中出现了所谓的严格 isNaN Number.isNaN
,在它的帮助下,上面的代码片段可以简化为 hasChanged = (x, y) => x !== y && !(Number.isNaN(x) && Number.isNaN(y))
实际上,不用window.isNaN
和Number.isNaN
,我们可以以更精简的方式获得相同的结果。因为当一个值的计算结果为 NaN 时,只有一个值可能不严格等于它本身。
function hasChanged(x, y) { x !== y && !(x !== x && y !== y) }
严格地讲,+0 和-0 是不同的
根据常识,+0
和-0
是相同的值,但在 JavaScript 中并非如此。例如,以下整数除法表达式将在 Java 中引发错误:
int i = 1; int positiveZero = +0; int result1 = i / positiveZero; // raise an ArithmeticException
然而,JavaScript 是一种动态类型编程语言,在上面的示例中,它与 Java 一样进行双重划分。
1/+0 === 1/0 === Infinity 1/-0 === -Infinity
但是+0
和-0
与严格相等运算符相比是相同的。
构建自己的 Object.is
现在,我们已经了解了Object.is
的所有特性以及它与相等/严格相等运算符之间的区别。让我们撸起袖子,构建自己的Object.is
。
Object.defineProperty(Object, 'is', { value(x, y) { return x === y ? 1 / x === 1 / y // +0 != -0 : x !== x && y !== y // NaN == NaN } })