Vue3源代码阅读:“hasChanged”是如何工作的?

目录
文章目录隐藏
  1. Object.is 是什么?
  2. NaN 是相等和严格相等的特殊值
  3. 严格地讲,+0 和-0 是不同的
  4. 构建自己的 Object.is

嘿,伙计们!下一代 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.isNaNNumber.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
    }
})

「点点赞赏,手留余香」

2

给作者打赏,鼓励TA抓紧创作!

微信微信 支付宝支付宝

还没有人赞赏,快来当第一个赞赏的人吧!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
码云笔记 » Vue3源代码阅读:“hasChanged”是如何工作的?

发表回复