在JavaScript中,为什么(-1).toString和(-1>>> 0).toString相同,但它们会给出不同的结果?

时间:2016-01-07 10:03:05

标签: javascript oop types interface

背景信息是:在JavaScript中,float和int之间没有区别。只有数字,它是IEEE 754标准。所以在JavaScript中不应该有任何“float”vs“int”。即使在按位操作时将数字视为32位整数,结果也应为Number。换句话说,不应该有一个“整数”的类型。

但至少在谷歌浏览器和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)

2 个答案:

答案 0 :(得分: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

现在你注意到他们的行为方式。

答案 1 :(得分:0)

这变成了您正在混淆一些事情...

为什么这个true

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

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

我想你的意思是:

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

是否注意到多余的括号?

就数字处理而言,正如FrédéricHamidi指出的那样,移位运算(以及按位运算)将Number转换为32位整数,进行数学运算,然后将其转换回Number。您可以使用诸如C ++之类的类型化语言来编写以下内容,以使其清晰明了:

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

static_cast<>()不是必需的,该语言会自动为您完成这些操作,但是为了清楚起见,我将其放在此处。

重要的一点是,>>>运算符假定输入整数是无符号的(因此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确实将无符号整数转换回Numberdouble)。如果查看(-1 >>> 0).toString()的输出,您会发现它不再是-1,而是4294967295。可以容纳32位(1 << 32 - 1。)的最大无符号数字。

现在,我想您理解了无符号移位问题,这意味着符号位丢失了。在8位的情况下,移位后看起来像这样:

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 ++程序员一样。)


P.S。您对__proto__的第二次测试也遇到了同样的问题。您正在比较参考,而不是内容。在JavaScript中,您有一个对数组和对象(and it can become hairy)进行深度比较的概念。 __proto__是一个对象,您只是比较了引用。话虽这么说,由于两者都是Number,因此深度比较也将返回true。