【问题标题】:In JavaScript, why is (-1).toString and (-1 >>> 0).toString identical, yet they give different results?在 JavaScript 中,为什么 (-1).toString 和 (-1 >>> 0).toString 相同,但它们给出不同的结果?
【发布时间】:2016-04-11 16:08:49
【问题描述】:

背景资料是:在JavaScript中,float和int没有区别。只有编号,它是 IEEE 754 标准。所以 JavaScript 中不应该有任何“float”和“int”。即使在进行按位运算时将数字视为 32 位整数,结果也应该是 Number。换句话说,不应该有“整数”类型。

但至少在 Google Chrome 和 Node.js 上:

比较2个函数(作为对象的函数):

(-1).toString === (-1 >>> 0).toString      // => true

所以它们是相同的、相同的函数对象。事实上,“被包裹的对象”属于同一类型:

(-1).__proto__ === (-1 >>> 0).__proto__    // => true

但是当你使用

(-1).toString(2)         // => "-1"

(-1 >>> 0).toString(2)   // => "11111111111111111111111111111111"

似乎对象包装器(使原始类型 1 成为对象的包装器)将创建具有相同对象类型的两个对象(如 __proto__ 所示),但不知何故,它认为本身不是同一类型,一个是浮点数,一个是 32 位整数。这是为什么?

根据OOP接口通常的一般原则,如果两个对象属于相同类型(相同类型,如__proto__表示相同),它们在响应时不应该给出相同、相同的结果吗?相同的“消息”,即toString(2)?

【问题讨论】:

  • JavaScript 使用单个 Number 类型来表示整数和浮点数。但是,来自MDN移位运算符将其操作数以大端顺序转换为 32 位整数,并且在应用零填充右移时,符号位变为 0,因此结果总是非负数。

标签: javascript oop types interface


【解决方案1】:

-1-1>>>0 都是“数字”类型

您可以分别从typeof -1typeof -1>>>0查看。

所以,他们的两个方法.toString 字面意思是Number.prototype.toString,完全相同。 这意味着所有以下都指的是同一个Number.prototype.toString

(1).toString // Number.prototype.toString
(2).toString // Number.prototype.toString
(1.55555).toString // Number.prototype.toString
(Infinity).toString // Number.prototype.toString (Yes, typeof Infinity is Number)
(NaN).toString // Number.prototype.toString (typeof NaN is Number)
(1>>0).toString // Also Number.prototype.toString

这就是为什么他们的方法.toString 的比较结果为真。他们都是Number.prototype.toString

它派生出相同的 Number.prototype.toString 并不意味着它总是产生相同的输出

该方法采用不同的输入值肯定会返回你不同的结果值

为了说明效果。调用它。

(-1).toString(2);

等同于:

Number.prototype.toString.apply(-1,[2]); // Yields -1

另外,这样称呼:

(-1>>>0).toString(2);

等同于:

Number.prototype.toString.apply(-1>>>0,[2]); // Yields 11111111111111111111111111111111

现在您注意到它们的行为方式了。

【讨论】:

  • 请在更新的问题中查看我的背景信息:不应该有整数类型。应该只有数字。那么为什么突然之间,似乎有一个类型 Integer 对象?
  • 为什么你期望一个整数(数学整数)的按位移位输出不是整数? JavaScript 中的那些是 32 位数字。结果仍然是 32 位数字。无符号位移位证明了这一点。结果是一个 32 位数字(您甚至可以仔细检查类型)。
【解决方案2】:

这就变成了你把事情搞混了……

为什么是true

(-1).toString === (-1 >>> 0).toString      // => true

因为两者都是NumberNumber.toString === Number.toString

我想你的意思是:

(-1).toString() === (-1 >>> 0).toString()      // => false!!!

注意到多余的括号了吗?

在数字处理方面,正如 Frédéric Hamidi 指出的那样,移位操作(和按位操作)将数字转换为 32 位整数,进行数学运算,然后将其转换回数字。您可以用 C++ 等类型化语言编写以下代码以使其清楚:

double d = -1;
uint32_t u = static_cast<uint32_t>(d)
u >>= 1
d = static_cast<double>(u)

static_cast&lt;&gt;() 不是必需的,语言会自动为您完成这些,但为了清楚起见,我将它们放在那里。

重要的一点,&gt;&gt;&gt; 运算符假定输入整数是无符号的(因此uint32_t)您可以认为应该使用有符号整数作为中间值,如下所示:

double d = -1;
int32_t i = static_cast<int32_t>(d)
uint32_t u = static_cast<uint32_t>(i);
u >>= 1
i = static_cast<int32_t>(u)
d = static_cast<double>(i)

但是,事实并非如此。 JavaScript 确实将无符号整数转换回Number (double)。如果您查看(-1 &gt;&gt;&gt; 0).toString() 的输出,您会注意到它不再是-1,而是4294967295。可容纳 32 位的最大无符号数 (1 &lt;&lt; 32 - 1.)

现在,我想您了解无符号移位问题,这意味着符号位丢失。在 8 位上,移动 1 位看起来像这样:

1111 1111 >>  1 = 1111 1111
1111 1111 >>> 1 = 0111 1111   (bit 7 becomes zero)

但是,在您的情况下,您使用了 0 位移,因此该值根本不会移动:

1111 1111 >>  0 = 1111 1111          signed, so it is -1
1111 1111 >>> 0 = 1111 1111          unsigned, so it is 255

但在第二种情况下,整数变为无符号数。由于在 double 尾数中有足够的空间来保存 uint32_t,因此您不会失去精度(一个有趣的 JavaScript 技巧!在 C++ 中,您当然希望找回符号当您将 uint32_t 保存在 int32_t 中时...如果您像 C/C++ 程序员一样思考。)


附注您对__proto__ 的第二次测试有同样的问题。您正在比较参考,而不是内容。在 JavaScript 中,您拥有数组和对象深度比较的概念 (and it can become hairy)。 __proto__ 是一个对象,您只是比较了引用。话虽如此,因为两者都是Numbers,所以深度比较也会返回 true。

【讨论】:

    猜你喜欢
    • 2021-10-04
    • 1970-01-01
    • 2021-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-29
    • 2021-10-23
    相关资源
    最近更新 更多