無標題文檔

Javascript 每日测试 - 第七期

这期的题目是

请问以下 alert 语句分别弹出的值?

var a = b = c = d = e = f = 1;
alert(a++);
alert(++b); 
alert(++ c);
alert(d ++);
alert(+ +e);
alert(+ + f);

这次主要考验大家对于操作符的理解,特别是一元操作符。让我们先来回顾下 Mozilla 官方 对于操作符方面的解释

下面再理解下 ++ 这个操作符号,通常我们都会将一元操作符直接加在变量的后面。类似的就变成了习惯,以至于对于这样的语句

alert(++                                         b);

就会产生怀疑。但是实际上,他们还是正确的( 来源 )。

「++ c 和 d ++ 只是运算符和运算数之间多了个空格,其实相当于++c和d++,同时建议大家除了 for (var i = 0; i < n; ++i) 这样的应用外,尽量少使用 ++ 和 --, 一来提高代码的可读性,二来避免某些不完善的 code formatter 导致错误」 -- from 小马。

那么上述问题的答案就非常明显了,分别是 1、2、2、1、1、1 。下面,再布置道家庭作业,来自 BT 的 小虎 同志 -_-!

var a = 1;
alert(- + - + + + + - + - - - - - - - + (-+-+-+-+-a++));

请问,上述的 alert 语句会弹出什么值?

Javascript 每日测试 - 第六期

这期的问题是

// 请问如下 alert 语句依次弹出的是什么?
alert('' == '0'); 
alert('' == false); 
alert('0' == 0);
alert('\n' == 0); 
alert(null == false); 
alert(null == undefined); 

此道题目的解释可以参考 《PPK on Javascript》 上的解释 -- form 玉伯:

These are the rules for converting other data types to booleans:

The values null and undefined become false.
The numbers 0 and NaN become false.
An empty string '' becomes false.
All other values become true.

在这里需要注意的是这句

An empty string '' becomes false. 

「empty string」 的含义包括不可见字符("n t v" 等),所以极端上考虑 alert("n t v" == 0) 这个也是 true 。

此道题目的最后答案是 false、true、true、true、false、true 。因此,「在不明确强制类型转换(或者要求判断结果比较高)时,尽量使用 === 替代 == 」 -- form 小马 。

-- Split --

另,外加一道 玉伯 的题目:

var testObj = {
    alert: function() { alert('2'); },
    init: function(el) {
        YAHOO.util.Event.on(el, 'click', function() {
            this.alert('1'); // ?
            (function() { this.alert('1'); })(); // ?
            (function() { this.alert('3'); }).call(this); // ?
        }, null, this);
    }
};

testObj.init(document);
// 问题: 点击 document,alert 依次输出什么?

这道题目需要注意三点:

  1. YAHOO.util.Event.on(el, eventType, fn, obj, override) 最后两个参数的使用
  2. 匿名函数中,this === window
  3. call 和 apply 的使用

最后答案为 「2, 1, 2」,相关参考资料:

Javascript 每日测试 - 第五期

今天的问题是

请问以下 alert 弹出值分别是什么?

var f = function f2() {
    return arguments.callee;
}

alert(f === f2);
alert(f === f());
alert(f() === f2());

让我们回顾 callee 的用法, Mozilla 官方的相关描述 如下:

callee is a property of the arguments local variable available
within all function objects; callee as a property of Function.arguments
is no longer used. (Function.arguments itself is also deprecated.)

arguments.callee allows anonymous functions to refer to themselves, 
which is necessary for recursive anonymous functions.

The this keyword does not refer to the currently executing function. 
Use the callee property to refer to a function within the function body. 

在 Javascript 中,一般定义某个函数有两种方式,分别是 function f() {...} 以及 var f = function () {...} 。同时,在 Javascript 中,函数可以当变量使用(Javascript 允许你结合两种方式使用,虽然这样的代码出现较少)。

var f = function f2() {
    return arguments.callee;
}

上面的代码可以分解为

function f2() {
    return arguments.callee;
}

var f = f2;

分解后的代码可以认为 f 与 f2 相同(==),但是进一步的理解他们并不完全相同(===)。调用 f() 与 f2() 执行获得的结果一样,「但 f2 这个变量保存了函数的整个内容,f 是对 f2 函数的引用」 -- form 丁坚。

其实这样的写法很少见,并不推荐在实际的项目中这样编写。说完上面的问题,回到我们的题目中。从上面的 Mozilla 的说明中可得知,arguments.callee 引用的是当前正在执行的函数本身。

所以,由此可以推断 alert(f === f()); 返回的是true;同理 f2 === f2() 返回的也是 true 。f() 返回的就是 f, f2() 返回的就是 f2, 所以第三个 alert() 等同于第一个 alert() 语法, 返回的也是 false 。

所以,本日 Javascript 测试的答案为:false、true、false 。

那么 arguments.callee 有无实际的用途?回答是肯定的。在匿名函数用得越来越多的情况下,很多时候 arguments.callee 能帮上大忙。

比如,希望某事件只执行一次,那么可以这样做(使用 YUI 框架):

YAHOO.util.Event.on(button, 'click', function(ev) {
    // ...
    // 注销自己
    YAHOO.util.Event.removeListener(button, 'click', arguments.callee);
});

YAHOO.util.Event.removeListener 的详细说明 参见这里 。我们给某个按钮注册了一个事件,时间处理器是一个匿名函数,点击一次以后,通过 removeListener 和 arguments.callee,就可以自我注销。

补充 -- form 玉伯

「也可以这样写就用不到 arguments.callee」,但这样代码感觉稍许冗余:

YAHOO.util.Event.on(button, 'click', function fn(ev) {
    // ...
    // 注销自己
    YAHOO.util.Event.removeListener(button, 'click', fn);
});

其次,arguments.callee 还有一个重要的用途,就是在递归,比如:

function factorial(n) {
    if (n <= 0) {
        return 1;
    } else {
        return n * arguments.callee(n - 1);
    }
}

参考资料:

--EOF--

Javascript 每日测试 - 第四期

这期的测试内容非常的有趣,下面慢慢的道来。题目是

var price = 100;
function doSomething() {
    var doublePrice = price * 2;
    var price = 200;
    var halfPrice = price / 2;

    alert(doublePrice);
    alert(halfPrice);
}
doSomething();

请问两个 alert 弹出值分别是多少?

不要轻易相信自己的判断,建议将这段代码放到浏览器运行下,相信结果会感到非常的意外 -- 答案是 doublePrice 是 NaN、halfPrice 是 100 。

会很奇怪为什么会是这样的结果,小虎 同学的解释为,「第一行 price 被下面的 var price 重新定义,所以它是未定义的」。代码可以认为如下:

var price = 100;
function doSomething() {
    var price;
    var doublePrice = price * 2;
    price = 200;
    var halfPrice = price / 2;

    alert(doublePrice);
    alert(halfPrice);
}
doSomething();

理解这个问题,要理解 Javascript 的作用域。已经存在一个全局变量 price, 按照其他语言的情况,doublePrice = 全局 price 变量 2;而事实上,Javascript 会先检查函数内部变量作用域,上述的代码中 doSomething() 内部也有个同名的 price 变量。于是 doublePrice = 内部 price 变量 2 (小马总结)。

某个变量如果未声明(没有 var)而直接使用,那在它的第一次被调用的作用域里(子作用域里不算)。只要有一个地方对其进行了声明,那它就属于该作用域,否则它的作用域会是上一级(直上到顶层) -- form 圆心 。

因此,需要注意 Javascript 这个特性。首先,得意识到「全局变量是魔鬼,大型的项目随着发展,(如果考虑不周全)变量的命名很有可能和函数中的临时变量同名」,建议适度使用闭包避免此问题。

其次在函数中,尽量把要用到的变量名写在函数的顶部。「这点也需要注意下的就是,虽然 Javascript 没有块作用域,但为了清晰起见,该在块里定义的变量,个人觉得还是在块里定义比较好」 -- form 玉伯 。

最后,再用 玉伯 的一道题结束本文:

 看看下面的代码,运行结果是什么?

function test() {
    alert(」test function 1″);
}

test();

function test() {
    alert(」test function 2″);
}

详细信息请 参见这里

--EOF--

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 结果是什么?

我的照片

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

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

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

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

分类

搜索

文章