無標題文檔

Javascript 每日测试 - 第三期

现在每日的 Javascript 提问时间,已经成为了部门前端开发人员的下午茶。这次的问题是:

请问下面的代码会弹出 true or false ?
<script type="text/javascript">
    var n1 = 0.1;
    var n2 = 0.2;
    var n3 = 0.4;
    var n4 = 0.8;

    alert((n1+n2+n3+n4) === (n1+n2)+(n3+n4));
</script>

https://friable.rocks/_/2008_07_07/1215423870.png

这是个很「诡异」的问题。乍看之下应该都是 true,不过「现实是残酷的」,这段代码在各浏览器中的运行结果如上图。

下面的解释还是能让人接受的:「简单来说就是二进制的浮点数难以正确处理十进制的小数」。

其实浮点数的精度问题,在大部分的开发语言中都能碰到(原谅我绝对了)。比如 PHP 中执行

<?php
    $a = 0.100000000000000000000000000000001;
    var_dump($a + 0.000000000000000001 == $a + 0.000000000000000002);
?>

以及 Java 中运行这样的代码

public static void main(String[] args) {
    System.out.print(0.1 + 0.2);
}

也会出现类似的问题(虽然例子可能有点极端,不过已经能够说明问题)。

那么,该如何解决该问题?「解铃还需经理人」,小马 同学在讲解答案的时候,给出了部分解决代码

Math.formatFloat = function(f, digit) {
    var m = Math.pow(10, digit);
    return parseInt(f * m, 10) / m;
}

alert(Math.formatFloat(0.1 + 0.2, 1));  // 0.3

玉伯的补充:

关于精度问题,一般涉及到小数点时,如果不是0.5, 0.25 这种反复乘以 2 会等于 1 的小
数,其它小数都无法无二进制精确表示,由此造成了误差。比如 10 进制的 0.1, 用二进
制表示是 0.0001100110011001100110...........  无限循环了。

对于进制方面更深一层的了解,请 参阅这里

那么,下面的测试

alert((0.1 + 0.2 + 0.4 + 0.8)    === ((0.1 + 0.2) + (0.4 + 0.8))); // 已经知道是false
alert((0.1 + 0.2 + 0.4 + 0.8)    === ((0.1 + 0.8) + (0.2 + 0.4)));    
alert((0.25 + 0.75 + 0.05 + 1.0) === (0.25 + (0.75 + 0.05) + 1.0));
alert((0.25 + 0.75 + 0.05 + 1.0) === ((0.25 + 0.75) + (0.05 + 1.0)));
alert((0.25 + 0.75 + 0.05 + 1.0) === ((1.0 + 0.75) + (0.05 + 0.25)));

其它的 alert 结果是什么?

Javascript 每日测试 - 第一期

天气越发炎热,中午搞个小测试给大家提神还是不错的。这次是 玉伯 兄弟出的题目:

请确认下述语句

alert(String.indexOf);
delete String.indexOf;
alert(String.indexOf);     
alert(String.length);    
String.prototype = null;
delete String.split;
delete String.prototype;
alert(String.prototype.split);
alert(String.split);

每个 alert 都会输出什么?

粗看这道题目考的是 prototype 原型以及 delete 操作符的用法,于是很快就写出了答案

alert(String.indexOf);  => function () {...}
...
alert(String.indexOf);   => undefined
...
alert(String.prototype.split); => function () {...}
alert(String.split); => undeinfed

有关 delete 操作符的描述,可以参见 Mozilla 的 相关文档

ECMAScript 为元素的属性定义了 4 种特性,它们分别是 ReadOnly、DontEnum、DontDelete、Internal ,这在平时用户级别的 Javascript 是无法更改的(来源)。(但 undefined 和 NaN 却是可读写的,这个是有点莫名其妙 -- form 小马)。

回到上面的问题,对于

alert(String.length);

输出 1 (不好意思作弊了)这个现象非常难以理解。小马 同学(再次感谢)做出了如下的解释,整理如下:

String 事实上是一个 function,是个所谓的构造器函数,可
用 typeof String 来验证。那么 function 的 length 属性,
它的值就是这个构造器函数在声明时的参数的数量。

详细可以 参考这里 。那么这样就很容易理解 alert(String.length) 为什么是 1 了 -- 因为它就提供了一个参数( 来源 )。

--EOF--

Javascript 每日测试 - 第二期

这回是 小马 同学出的题目:

var a = parseInt('01');
alert(a == 1); //true
var b = parseInt('09/04/2008');
alert(b == 9); //?

请问第二个会弹出 true 还是 false ?

首先是 沈冲 同学的回答:

如果 string 以 0 开头,那么 ECMAScript v3 允许 parseInt() 的一个实现
把其后的字符解析为八进制或十六进制的数字。

佩玉 同学就更牛了,直接转贴了 Javascript 犀牛书上的内容:

parseInt( ) parses and returns the first number (with an optional leading
minus sign) that occurs in s. Parsing stops, and the value is returned, 
when parseInt( ) encounters a character in s that is not a valid digit for
the specified radix. If s does not begin with a number that parseInt( ) can
parse, the function returns the not-a-number value NaN. Use the isNaN( ) 
function to test for this return value.

总结,parseInt 其实有两个参数( 详细 ):

parseInt(string[, radix])

所以当第一个参数遇到 0 开头的字符时,会自动使用 8 进制解析数字,而 9 不是 8 进制里合法的数字,所以返回 0 。所以要在 parseInt 函数中数指定进制(也就是第二个参数),才能得到期望的结果。

为了代码的健壮性,建议使用 parseInt 时总是指定第二个参数 -- 小马。

--EOF--

我的照片

嗨!我叫「明城」,八零后、码农、宁波佬,现居杭州。除了这里,同时也欢迎您关注我的 GitHubTwitterInstagram 等。

这个 Blog 原先的名字叫 Gracecode.com 、现在叫 「無標題文檔」 。 要知道作为码农取名是件很难的事情,所以不想在取名这事情上太费心思。

作为八零后,自认为还仅存点点可能不怎么被理解的幽默感,以及对平淡生活的追求和向往。 为了避免不必要的麻烦,声明本站所输出的内容以及观点仅代表个人,不代表自己所服务公司或组织的任何立场。

如果您想联系我,可以发我邮件 `echo bWluZ2NoZW5nQG91dGxvb2suY29tCg== | base64 -d`

分类

搜索

文章