论腾讯 TFC 抽奖程序作弊的可能性,以及代码中存在的 bug
2017-06-26 22:57
260 查看
腾讯 TFC 前端大会已经圆满结束,除了我的航班一直延误之外,还有 2 个遗憾:一个是 V8 定制版的 T 恤没有按时寄到,另一个就是没有抽中 iPhone 7。
但是抽奖代码绝对是会场的一大亮点,而且在场的所有参会者也有幸参与了对此次抽奖程序进行 code review,(认真脸)
当抽奖代码公布后,全场惊呼加爆笑。
主持人首先输入
作用是为了确认
第一轮的抽奖程序是:
很多人开始在群里问:前面的两个波浪号是干什么的啊?
如果在抽奖之前听了我的 V8 topic 就会知道位运算要比
我在 V8 性能优化里面讲到过一个关键词——“截断”,在 ECMA 规范中对按位取反的定义:
产生式 UnaryExpression : ~ UnaryExpression 按照下面的过程执行 :
令 expr 为解释执行 UnaryExpression 的结果 .
令 oldValue 为 ToInt32(GetValue(expr)).
返回 oldValue 按位取反的结果。结果为 32 位有符号整数。
不仅仅是
如果把这个运算和
返回不大于
若
若
若
若
若
若
根据定义,
因为
我们可以比较他们的倒数,因为
虽然如何,我们在特定的场景依然推荐使用
作为抽奖程序我们最关注的就是能不能作弊。抽奖之前的操作:
也是为了向观众说明,这段代码没有作弊。
果真如此吗?
如果我们重写了 random 函数,那么这段代码就会把我们的函数打印出来:
这样一下子就暴露了。那么我们有没有办法既重写了 random 函数,又不让用户知道我们重写了这个函数。答案是肯定的。我们可以继续重写
我们先运行:
然后当我们在控制台输入以下语句时就会神不知鬼不觉了:
如果我以后再参加抽奖互动时突然获得了特等奖,那纯属巧合。
但是抽奖代码绝对是会场的一大亮点,而且在场的所有参会者也有幸参与了对此次抽奖程序进行 code review,(认真脸)
当抽奖代码公布后,全场惊呼加爆笑。
主持人首先输入
Math.random然后回车,控制台输出:
> Math.random
function random() { [native code] }
作用是为了确认
Math.random是原生的代码,没有被重写过。
第一轮的抽奖程序是:
~~(0 + Math.random() * 51)
很多人开始在群里问:前面的两个波浪号是干什么的啊?
~是按位取反,这段代码使用了一个位运算的小技巧:通过 2 次按位取反来实现向下取整。
如果在抽奖之前听了我的 V8 topic 就会知道位运算要比
Math.floor性能高出很多。但是细心的同学也会发现,这段代码存在 bug。
我在 V8 性能优化里面讲到过一个关键词——“截断”,在 ECMA 规范中对按位取反的定义:
产生式 UnaryExpression : ~ UnaryExpression 按照下面的过程执行 :
令 expr 为解释执行 UnaryExpression 的结果 .
令 oldValue 为 ToInt32(GetValue(expr)).
返回 oldValue 按位取反的结果。结果为 32 位有符号整数。
不仅仅是
~,所有位运算的结果都是 32 位有符号整数。如果我们运行
~~(2**31)就会得到一个负数
-2147483648。不过,即使全中国都来参加这次大会,程序也不会出 bug 的。
如果把这个运算和
Math.floor对比,会发现两者的差异不仅仅是表示范围不同:
返回不大于
x的且为数学整数的最大 ( 接近
+∞) 数字值。如果
x已是整数,则返回
x。
若
x是
NaN, 返回结果是
NaN.
若
x是
+0, 返回结果是
+0.
若
x是
−0, 返回结果是
−0.
若
x是
+∞, 返回结果是
+∞.
若
x是
−∞, 返回结果是
−∞.
若
x大于
0但小于
1, 返回结果是
+0.
~的结果是 int32 的有符号整数,所以肯定不可能是 NaN 和无穷,因此 1、4、5 两者不同。
根据定义,
Math.floor向
+∞取整,比如
Math.floor(-1.5)的结果是
-2,因为
-2是不大于
-1.5的最小的整数。而
~~(-1.5)的结果是
1。
因为
~的结果是整数,所以只有一个 0。(PS:正零和负零是浮点数里面的概念)。在 javascript 中
+0完全等于
-0,那么怎么分区两者呢?
> 0 == -0
true
> 0 === -0
true
我们可以比较他们的倒数,因为
+0的倒数是
+∞,
−0的倒数是
−∞:
> 1/0
Infinity
> 1/-0
-Infinity
> 1/0 == 1/-0
false
> 1/0 === 1/-0
false
虽然如何,我们在特定的场景依然推荐使用
~~运算来向下取整。首先位运算本身就会比函数调用快很多,在加之规范对 floor 的各种特殊值处理也导致了性能损失。我之前在 JavaScript 函数式编程的性能问题 中也分析过,直接使用 for 循环要比数组的 forEach 快 20 倍以上,也是类似的原因。
作为抽奖程序我们最关注的就是能不能作弊。抽奖之前的操作:
> Math.random
function random() { [native code] }
也是为了向观众说明,这段代码没有作弊。
果真如此吗?
如果我们重写了 random 函数,那么这段代码就会把我们的函数打印出来:
Math.random
() => '中奖用户是 jjc'
这样一下子就暴露了。那么我们有没有办法既重写了 random 函数,又不让用户知道我们重写了这个函数。答案是肯定的。我们可以继续重写
Math.random.toString方法。
我们先运行:
Math.random = () => '中奖用户是 jjc'
Math.random.toString = () => 'function random() { [native code] }'
然后当我们在控制台输入以下语句时就会神不知鬼不觉了:
> Math.random
function random() { [native code] }
> Math.random()
"中奖用户是 jjc"
如果我以后再参加抽奖互动时突然获得了特等奖,那纯属巧合。
相关文章推荐
- iOS 利用腾讯 Bugly 程序调试 测试代码bug 卡顿等情况
- 《水晶之约》的完整代码以及一些思想很值得学习---优秀程序的代码固然能更提高自己
- VS2005开发本地代码Smartphone程序,菜单编辑器有严重Bug
- Bug:正试图在 OS 加载程序锁内执行托管代码
- 项目2:PHP抽奖程序 ,抽奖规则代码
- 使用铁哥SmartFlash快速开发方案:66行代码搞定抽奖程序!
- 二级域名原理以及程序代码
- 修改Keil C 程序代码字体 修改Keil C 微软雅黑 以及 Keil C 光标定位不准的解决办法
- javascript 随机抽奖程序代码
- javascript 随机抽奖程序代码
- 项目3:PHP抽奖程序 ,抽奖规则代码 分时间段
- 如何在ASP.NET中用程序代码设定DATAGRID每列的宽度以及折不折行
- 页面自动刷新以及广告程序得Javascript代码
- CSDN博客中插入代码存在一个BUG,望大家注意下
- javascript -- 发现之前开发的paypal跳转伪装下单站的程序存在重大的BUG
- 一个WinForm记事本程序(包含主/下拉/弹出菜单/打开文件/保存文件/打印/页面设置/字体/颜色对话框/剪切版操作等等控件用法以及记事本菜单事件/按键事件的具体代码)
- 二级域名原理以及程序代码
- 一个WinForm记事本程序(包含主/下拉/弹出菜单/打开文件/保存文件/打印/页面设置/字体/颜色对话框/剪切版操作等等控件用法以及记事本菜单事件/按键事件的具体代码)
- Bug:正试图在 OS 加载程序锁内执行托管代码
- 【转载】使用铁哥SmartFlash快速开发方案:66行代码搞定抽奖程序!