無標題文檔

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--

我的照片

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

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

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

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

分类

搜索

文章