好几年前学 javascript 的时候,网上都说 js 内部数字表达方式就一种,用浮点来表示。
然而我看了 google 的代码后,对这个观点,表示强烈怀疑。
这是 google 的代码库:https://github.com/google/closure-library/blob/master/closure/goog/math/long.js
开头明确写着是 32 位整型:
this.low_ = low | 0; // force into 32 signed bits.
this.high_ = high | 0; // force into 32 signed bits.
我又去翻了一下各种资料,顺藤摸瓜,又找到了类似的文章:
JS 类型声明有固定写法,变量 | 0 表示整数,+变量表示浮点数。
var a = 1;
var x = a | 0; // x 是 32 位整数
var y = +a; // y 是 64 位浮点数
上面代码中,变量 x 声明为整数,y 声明为浮点数。支持 asm.js 的引擎一看到 x = a | 0,就知道 x 是整数,然后采用 asm.js 的机制处理。如果引擎不支持 asm.js 也没关系,这段代码照样可以运行,最后得到的还是同样的结果。
再看下面的例子。
// 写法一
var first = 5;
var second = first;
// 写法二
var first = 5;
var second = first | 0;
上面代码中,写法一是普通的 JavaScript,变量 second 只有在运行时才能知道类型,这样就很慢了,写法二是优化 js,second 在声明时就知道是整数,速度就提高了。
也许那么多年,chrome 内核早就对 js 运行期做了一定智能优化,再也不是傻傻的无类型语言了。感叹 JS 还真是在不断进化中。
确实有优化,v8 里叫做小整数,简称 SMI
(不过是有符号的,所以是 -2**32 到 2**32 - 1 的范围)
放数组里效果最明显,在没有 Uint8Array 等一系列数组的时候,就用纯数字数组也能实现很高的执行效率
可以看这里
https://v8.dev/blog/elements-kinds
hmmm 打错了,是 2**31(
大佬 V5,那篇文章看下来还挺复杂的。
js 再加个 int 关键字就好了,现在越来越复杂了。
不是很懂 阮一峰的 es6 教程写的是“JavaScript 所有数字都保存成 64 位浮点数”
https://es6.ruanyifeng.com/#docs/number#BigInt-%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B
标准和浏览器的实现可能是两回事吧
google 的官方代码应该没错。
也许从 chrome 某一版本开始,JS 内部语法就支持 var second = first | 0;这种特殊的整型数字定义,只是我们大部分人都不知道罢了。
这是对注释的误解。参考 MDN:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number
JavaScript 的 Number 等同于 IEEE 754 双精度浮点数。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_OR
JavaScript 的按位或运算先把运算数转换成 32 位整数再运算,得到的是 Number 。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Unary_plus
JavaScript 的正号运算把运算数变成 Number 。
你写 var y = 1, z = +1, w = 1.0; 都是没有区别的。另外 JavaScript 的引擎的 “内部” 不需要 “用 IEEE 754 双精度浮点数” “表示” 一个 Number,只要执行效果和 IEEE 754 双精度浮点数一致即可。
楼主说的问题是特定于 asm.js 的人肉优化手段。另外 JavaScript 从来都不是“无类型”的。
我肯定信 google 不信你啊。
我绝对不相信 google 写出的 js 代码,会把 double 笔误写成 signed 32 bits of the long 。人家谷歌可是开发浏览器的,肯定不会犯这种低级错误。
而且 google 代码也不是编译成 asm.js ,和这个没关系。
> JS 类型声明有固定写法,变量 | 0 表示整数,+变量表示浮点数。
哪里看的垃圾文章,误人子弟。“| 0”表示对 a 和 0 进行位操作“或”,运算时,只是在 VM 里把两个操作数转成 32 整数计算,计算后回到 JS 里依然是个浮点数,只不过值在 32 位整数范围(就是 Google 那句注释的意思)。
> var first = 5;
> var second = first | 0;
别笑死人了,某些 JS 转换器甚至做 tree shaking 会优化成 var second = 5 。
js 里只有浮点数和 bigint 两种数字类型
刚看明白,后面那段代码和 Google 没关系。但你对 Google 注释的误读仍然成立,若 a 是 Number,则 JavaScript 表达式 a | 0 对应 C表达式
double.IsNaN(a) ? 0.0 : (double)(int)(a)
其中假设 a 在 C里具有静态类型 double 。所谓 force into 32 signed bits 是指数值上的截断,不是类型上的改变。

我用游戏内存修改工具,查了一下 chrome v85 的 var abc = 12345678 | 0; 的内存布局。内部确实是 int32 整型表示的。
"所谓 force into 32 signed bits 是指数值上的截断,不是类型上的改变。"
尾巴上的|0, 就是个内部类型 hint 。代表了内部优先用 int32 位来表示。
JS 有运行期静态分析,如果函数里有复杂计算,Number 内部应该会自动退化到 double 。
标准和实现分开说,就不用争论了。
你确实存在误解,说的是对的。如果我没有猜错,你在 中的例子去掉 | 0 也是同样的效果,这个优化跟 | 0 完全没有关系,| 0 是应该说是某些场景下面给 compiler 的 hint 。
js 引擎的优化多了去了,规范还规定字符串都是 UTF-16,难道 js 引擎内部真的都用 UTF-16 存每个字符串么?只要在外部看起来符合规范,内部用什么黑科技优化都可以。
@3dwelcome
显然类型上是不会改变的,但是实现只要保证可观测的效果一致就可以了,内部用 smi 优化,对外表现还是和 double 一样,一点问题都没有
类似的优化在 v8 里还有字符串,被细分成单字节编码(仅 ASCII ),双字节编码,乃至还有给 JSON 留的特殊优化路径,这并不影响外部表现是 utf-16 字符串这件事。
只有在需要的时候,才会退化成性能表现更差的内部表示(然后同一个值再也不会回到优化的状态了)
@lujjjh
v8 的 JIT 会根据输入参数的内部表示类型做代码生成,一旦传入非 smi 数字,就有可能导致先前生成的代码失效从而影响性能,内部显式使用|0 之后,即使传入非 smi 数字,但是因为没有产生可观测的差异,所以不会破坏 JIT
数字内部表示是 int64,但是在进行位运算时会被强转成 int32
这是 ECMA 里面位操作明确定义的
是 double 不好意思,位运算强转 int32
是啊,再也不是傻傻的无类型语言了。我们 JS 真的是太厉害啦!
这个显然是优化,不然应当是一重间接指针的(因为是对象,scope -> number object -> stored data )
asm.js 的 `| 0` 的目的是入参强转,使得之后的代码可以很方便地 JIT
随手试了下 IE,发现根本找不到(
谁能告诉我 Cheat Engine 怎么找 IE 的内存?
哦,找到了
IE 似乎确实是两层指针的

但如果是 Double 且足够大了以后又是原位赋值了