更深入一点,变量赋值和严格相等如何在 Javascript 中工作

Posted

技术标签:

【中文标题】更深入一点,变量赋值和严格相等如何在 Javascript 中工作【英文标题】:How variable assignment and strict equality work in Javascript, in a little more depth 【发布时间】:2018-07-02 13:09:42 【问题描述】:

我们都知道JS有objects和一组primitives

让我们检查以下作业。

a = 'foo';
b = a;
c = 'foo'
a === b; //true
a = b = c = undefined;

我多年来在 JS 中可视化变量赋值过程的方式是这样的。

a = 'foo';

一个新的原始字符串foo 被创建并添加到一个内存位置。变量a 现在指向内存中的那个位置。

b = a

变量b 指向与a 相同的内存位置,这意味着它指向foo

c = 'foo'

原始字符串foo已经存在于内存中,所以变量c只是指向那个

a === b; //true

变量ab 通过引用进行比较,这意味着唯一要检查的是它们是否指向内存中的相同位置,而不是它们的值是否相同。这意味着无论字符串有多长,这都应该是一个快速的操作。

a = b = c = undefined

变量abc都指向原语undefined。原语foo 没有被任何变量指向,所以它被垃圾回收了。

以上所有内容也应适用于对象属性。

我 101% 确信这就是 JS 的工作原理,直到今天我与一位同事交谈时,他和我一样确信当比较两个变量时,即使是严格相等 (===) ,它们是按值比较而不是按引用比较

因此,如果我们必须比较两个都分配了非常大的字符串的变量,例如

a = 'a huge string';
b = 'a huge string';
a === b; //true, takes 1ms

与两个变量只分配一个字母字符串相比,这种相等性检查将花费更长的时间。例如

a = 'a small string';
b = 'a small string';
a === b; //true, takes 0.1ms

总结

我的任何要点(我的理解)是否不正确?严格相等是否比较值而不是引用?

欢迎并鼓励技术性回答。

谢谢。

【问题讨论】:

您的假设有一个重要的警告。您在某种程度上将单个 JS 引擎实现细节与官方 ECMAScript 规范混为一谈(规范中并未概述所有这些行为) 如果我正确理解了您的问题,重点是字符串和数字是按值进行比较(并为此分配),而对象(数组、函数)是按引用进行比较(并分配)。考虑var a = ; var b = a !== b。正如您所展示的,对于字符串,它们是相等的。 【参考方案1】:

基于 ECMAScript 规范,=== 比较值,而不是引用,除非这些值是对象。

对于字符串比较,它们按字典顺序进行比较,就像检查所有字符是否按相同顺序相等。

如果 x 和 y 是完全相同的代码单元序列(相同的长度和对应索引处的相同代码单元),则返回 true;否则返回false。

这是a === b 的一般算法:

// if both are undefined, return true
// if both are null, return true
// if both are numbers
//    if a is NaN, return false
//    if b is NaN, return false
//    if are equal numeric values, return true
//    if a is +0 and b is -0, return true
//    if b is +0 and a is -0, return true
//    return false
// if both are strings
//    if all chars equal and in the same order, return true
//    return false
// if both are booleans
//    if both are true or both are false, return true
//    return false
// if both are objects
//    if are equal references, return true
//    return false
// return false

我不久前写了一个 small tool,演示了可能有帮助的 ===== 算法(根据 ES5 规范)。

现在,就从已定义的字符串中重用内存而言,例如您的示例 a = 'foo'c = 'foo' 出于效率原因指向相同的内存,这是引擎可以进行的优化以使事情变得更快,但不是规范的一部分。

其实这叫String Interning,很多语言都这样做。

【讨论】:

这肯定是一个令人大开眼界的答案,谢谢。所以,从技术上讲,我的同事是对的 :) 好吧,再一次,这是理论和实践之间的一条细线:)。实际上,大多数引擎都会进行字符串实习,所以我想说你们都需要先定义判断规则,然后再决定谁是对的哈哈【参考方案2】:

当您使用原始类型(数字、字符串、布尔值)时,您是在比较它们的值。

"string one" == "string one";  // true
3 == 3;  // true
false == false;  // true

即使是未定义的作品也类似:

undefined == undefined;  // true

NaN 除外:

NaN == NaN;  // false

当您比较对象时,您会比较它们的引用。例如:

var obj1 =  "name": "Charles" ;
var obj2 =  "name": "Charles" ;
obj1 == obj2;  // false
obj1 === obj2;  // false

【讨论】:

以上是关于更深入一点,变量赋值和严格相等如何在 Javascript 中工作的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript深入浅出补充——语句和严格模式,对象

ECMAScript6 入门 变量的解析赋值

PHP 等效于 Perl 的“使用严格”(要求在使用前初始化变量)

变量的三个特征和花式赋值

[踩坑回顾]vue+elementUI开发时,变量名类型严格相等匹配

js-20170605-基本语法