無標題文檔

Javascript 每日测试 - 第十期

请问以下代码的弹出值?

function testFunction() {
    var args = Array.prototype.slice.call(arguments, 1, 2);
    alert(args);
}

testFunction(1,2,3);

本道题的目的是为了温习三个知识:

  1. arguments 是一个类数组,但不是一个数组
  2. 使用Array.prototype.slice 可以将 arguments 转换为一个数组
  3. slice 方法接受两个参数 start 和 end

使用 Array.prototype.slice 将 arguments 转换为数组可看作是个 hack。我们可以去追根溯源,了解些前因后果。

ECMAScript 规范 94 页对 Array.prototype.slice 有一个备注

The slice function is intentionally generic; it
does not require that its this value be an Array
object.

Therefore it can be transferred to other kinds of
objects for use as a method. Whether the slice

function can be applied successfully to a host object
is implementation-dependent.

Generic 可以理解为 Java 里的泛型 。对数组来说,就是在 JavaScript, 只要一个对象有 length 属性且可用下标的方式取得所包含的元素,那么它就是 Generic 。

所以使用 Array.prototype.slice.call(arguments) 可以得到一个数组, 除 slice 外, 根据测试使用数组的 splice 方法也可以实现类似的效果

Array.prototype.splice.call(arguments, 0, arguments.length)

同时,使用 Array.prototype.slice 时要注意, 第一个参数是 start,第二个参数是 end。 第二个参数可以不指定,那么表示取出全部。但不能为 null 或 undefined,否则将得到一个空数组( 详细 )。

「篱式」条件判断(翻译)

翻译自: http://gtc.motiveshq.com/2008/05/safety-fence-for-accessing-properties/

我们已经知道,null 没有任何的属性值,并且无法获取其实体(existence)值。所以 null.property 返回的是错误(error)而不是 undefined 。

考虑下面的代码

if (node.nextSibling.className == ...) {
   ...
}

在 node 或者 node.nextSibling 为空(null)的情况下,会返回错误(error)。所以,通常情况下的解决方案的代码为

if ((node) && (next = node.nextSibling) && ... ) {
   ...
}

那么,当条件判断一多的情况下,代码会形成下面的情况

if (
(node) &&
(node.nextSibling) &&
(node.nextSibling.className == ...)
... ) {
   ...
}

随着判断条件的不断的增加,代码会变得非常的「丑陋」。

有个小的「伎俩」,可以简化条件判断表达式。我们可以增加个空对象({})或者零(0)作为替代

if ( next = (node || 0).nextSibling) ) {
   ...
}

那么,上述的代码就可以这样写

if (((node || 0).nextSibling || 0).className == ...) {
   ...
}

--Split--

就个人而言,上述的从某种角度而言,代码会非常的精简。但日常实际的编码过程中,尤其是多人配合的情况下,这些代码可能会给其他开发人员造成一定的困扰。

正如 小马 所言,如果已经在使用某些框架,需要具体问题具体分析。比如上述的条件判断代码,使用 YUI 编码就可以使用

YAHOO.util.Dom.hasClass(el, className)

显得更加的精简,并且相比上述的代码更容易理解。

Javascript 每日测试 - 外一篇

这是期前讨论的,由于比较琐碎,所以整理在一起。

大家认为如下的函数会返回什么?

function test() {
    return
    {
        status: true
    };
}

答案是返回 undefined,因为 return 后面有回车(被认为是条完整的语句),可以认为等效下面的代码

function test() {
    return;
    {
        status: true
    };
}

清羽 同学从 ECMA 上找到了相应的解释

ReturnStatement:
    return [no LineTerminator here] Expressionopt ;    ——from Ecma-262

-- Split --

var Obj = {};
alert(Obj.abc == undefined); //true 

undefined = 'hello, world';
alert(undefined == 'hello, world'); //true

Obj.abc = 'hello, world';
alert(Obj.abc == undefined);

上述的代码最后 alert 的结果为 true 还是 false ?

在 Mozilla 相关的 Javascript 文档中, 对于 undefined 的描述 中可以得知

undefined is a property of the global object, i.e. it is a variable in global scope. 

The initial value of undefined is the primitive value undefined.

所以,「undefined」 的值是可以被覆盖的。所以,上述的答案为 true 。另,相对于「undefined」,null 则为关键字( 来源

null, a special keyword denoting a null value; null is also a primitive value. 
Because JavaScript is case-sensitive, null is not the same as Null, NULL, 
or any other variant

「同样的还有NaN, 可以试一下:」from 小马( 相关解释 )。

var a = parseInt('hello123'); 
alert(a); //NaN

NaN = 'hello123';
alert(NaN == 'hello123'); //true

var b = parseInt('hello123'); 
alert(b == 'hello123');  //?

-- Split --

本期最后一个问题:

<script>
function doClick() {
    alert(1);
}
</script>
<a href="#" onclick="执行:doClick();">测试</a>

上述的代码会执行(弹出 1)吗?详细描述请参看 http://www.hedgerwow.com/360/dhtml/js_label/

-- EOF --

Javascript 每日测试 - 第九期

今天的题目「看起来」没有任何的悬念。

请问点击第三个链接时弹出的值?

<div id="demo">
    <a href="#">A</a>
    <a href="#">B</a>
    <a href="#">C</a>
    <a href="#">D</a>
</div>

<script type="text/javascript">
var addHandlers = function(nodes) {
    for (var i = 0; i < nodes.length; ++i) {
        nodes[i].onclick = function(ev) {
            alert(i * 100);
        }
    }
}

addHandlers(document.getElementById('demo').getElementsByTagName('a'));

「很不幸」的是,上述的这段代码没有按照原意实现,点击它们所有的链接都是为 400。这是因为「点击以后执行 i * 100 ,而这个 i 是循环结束以后的值,按照上面的循环后值为 4」。

如果按照期望的写法,也就是点击链接以后 alert 不同的值,可以用闭包。比如下面有几个解决方案

var addHandlers = function(nodes) {
    for (var i = 0; i < nodes.length; ++i) {
        (function () {
         var j = i * 100;
            nodes[i].onclick = function(ev) {
                alert(j);
            }
        }) ();
    }
}

期前也讨论过 ,只不过那时还没有了解闭包,详细」。类似的题目,在 《精通 Javascript》 中 23 页( 中文版 )有提及。

var addHandlers = function(nodes) {
    for (var i = 0; i < nodes.length; ++i) {
        nodes[i].onclick = function(idx) {
            return function(ev) {
                alert(idx * 100);
            }
        }(i);
    }
}

「另,如果是使用 YUI 的话,可能会更直观些」

var addHandlers = function(nodes) {
    for (var i = 0; i < nodes.length; ++i) {
        YAHOO.util.Event.on(nodes[i], 'click', function(ev, idx) {
            alert(idx * 100);
        }, i);
    }
}

使用闭包的话,必须注意两点:其一,就是避免出现无谓的匿名函数;其二,就是注意内存溢出。下面有几条有关闭包的参考链接。

Javascript 每日测试 - 第八期

继续我们今天的 Javascript 每日测试时间,今天的题目是

<ul id="demo">
    <li>a</li>
    <li>b</li>
    <li>c</li>
</ul>

<script type="text/javascript">
var ul = document.getElementById('demo');
var liList = ul.getElementsByTagName('li');

for (var i = 0; i < liList.length; ++i) {
    ul.removeChild(liList[i]);
}
</script>

请问脚本执行的结果是什么?

在 PPK 这本书中提及过 ,getElementsByTagName(tag)返回的 HTML 数组 是会自己动态更新的,并非静态的列表。」 -- from 明城

「更正一下,其实更准确的表述,getElementsByTagName(tag)返回的是 NodeList, 而不是 Array 。」 -- from 小马

如何避免这样的问题?其一,可以换一种循环方式,使用 --i,或者 while 循环,只要代码清晰可读。比如

for(var i = liList.length - 1; i >= 0; i--) {
    ul.removeChild(liList[i]);
}

这样就可以解决上述的问题( 来源 )。

其二,是看情况。比较极端的例子,比如是希望把所有子节点都清除的话,可以直接使用 玉伯 提供的方法

parentNode.innerHTML = '';

有关 innerHTML 效率问题,以目前的电脑硬件而言,排除异常复杂的页面或者逻辑,性能方面应该是非常理想的。

当然,有关 innerHTML 效率方面,还可以改进提高 」 -- from 圆心。

我的照片

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

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

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

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

分类

搜索

文章