4

我一直在查看一些 jQuery 源代码,并且遇到了该merge函数。这是它的源代码:

function merge(first, second) {
    var l = second.length,
        i = first.length,
        j = 0;

    if (typeof l === "number") {
        for (; j < l; j++) {
            first[i++] = second[j];
        }
    } else {
        while (second[j] !== undefined) {
            first[i++] = second[j++];
        }
    }

    first.length = i;

    return first;
}

虽然我理解代码,但有些东西对我来说没有意义。特别是if (typeof l === "number")部分。当我手动将.length属性更改为类似"3"并检查它的类型时,我尝试将数组传递给该函数,但我仍然得到一个number.

length我的问题是该属性何时不会是numberJavaScript 数组中的一种类型?

4

3 回答 3

3

merge您正在查看的函数是一个公共 jQuery 方法:jQuery.merge()$.merge(). 因此,您可能认为阅读文档会对这个问题有所了解jQuery.merge()

不幸的是,至少在撰写本文时并非如此。

正如其他答案和评论中所解释的,当是 an时,这个对数字length属性的测试总是会成功,因为 an总是有一个 numeric 。在代码中进行此测试的唯一可能原因是处理is not an的情况。secondArrayArraylengthsecondArray

但是文档根本没有提到这种情况。它只讨论两者first都是second原生 JavaScriptArray对象,而不是“类数组”对象的情况。

如果这个记录的用法就是这个函数所做的一切,那将是毫无用处的,因为 JavaScript 提供了一个非常好的本机array.concat(array)方法,可以用来代替jQuery.merge().

所以看起来这个函数的真正目的必须是在它的无证使用中。让我们在jQuery.merge() 文档页面的 Chrome 控制台中尝试一下。打开开发者工具,选择 Console 选项卡,然后输入一个类似于文档中的示例:

jQuery.merge( [ 'a', 'b' ], [ 'c', 'd' ] )

然后尝试同样的事情.concat()

[ 'a', 'b' ].concat([ 'c', 'd' ])

他们都将记录完全相同的内容:

["a", "b", "c", "d"]

由于这是 jQuery,因此“类数组”对象的最可能情况是 jQuery 对象。例如,该文档页面当前包含三个<h3>元素,因此我们可以通过以下方式获取它们:

$('h3')

这将记录:

[►&lt;h3>…&lt;/h3>, ►&lt;h3>…&lt;/h3>, ►&lt;h3>…&lt;/h3>]

这不是一个数组,但它看起来很像一个数组,而且它确实有一个数字length属性,我们可以用它来检查:

typeof $('h3').length

所以让我们尝试一下concat

['x'].concat( $('h3') )

哎呀。这应该给我们一个包含四个元素的数组,'x' 后跟三个 DOM 元素。相反,我给了我们一个包含两个元素的数组,jQuery 对象作为第二个元素:

[►e.fn.init[3] ]

看起来 jQuery 的“类数组”对象对于.concat().

奇怪的是,其他一些数组方法可以处理 jQuery 的类数组对象,例如.slice()

Array.prototype.slice.call( $('h3'), 1 )

这记录了正确的结果,一个包含两个元素的数组:

[►&lt;h3>…&lt;/h3>, ►&lt;h3>…&lt;/h3>]

所以让我们试试$.merge()

$.merge( ['x'], $('h3') )

果然,这记录了我们的预期:

["x", ►&lt;h3>…&lt;/h3>, ►&lt;h3>…&lt;/h3>, ►&lt;h3>…&lt;/h3>]

还有另一种方法可以做到这一点。jQuery 提供了一个.toArray()方法,所以我们可以通过将它与 结合来做同样的事情.concat()

['x'].concat( $('h3').toArray() )

这记录了与通话相同的内容$.merge()

如果first是一个 jQuery 对象呢?

$.merge( $('h1'), $('h3') )

这也有效:

[<h1 class="entry-title">jQuery.merge()</h1>,
   ►&lt;h3>…&lt;/h3>, ►&lt;h3>…&lt;/h3>, ►&lt;h3>…&lt;/h3>]

所以看起来这就是这个方法的目的:array.concat()对 jQuery 对象做类似的操作。

可是等等!这仍然没有回答为什么非数字的额外代码在那里的问题.length。毕竟,一个 jQuery 对象确实有一个 numeric .length,所以我们还没有练习非数字情况的代码。

因此,这部分代码必须用于处理 jQuery 对象以外的其他目的。那会是什么?

我们可以通过访问jQuery 源存储库src并在包含代码的目录中找到文件merge:(它恰好是core.js)来了解更多信息。然后使用Blame按钮并在页面中搜索merge:代码以查看代码的提交注释是什么。同样,我们现在想知道的代码是else子句,其中typeof l不是数字。

该提交John Resig于 2009 年 12 月 9 日提交,并附有评论“Rewrote merge()(现在更快、更不钝)。已修复 #5610。” 该页面上的讨论存在一些争议:

好吧,您的版本仅在覆盖长度属性的特殊(非常罕见)情况下更快。在所有其他情况下,我的速度都快一点。此外,您没有考虑 obj.length = new Number(N) 的情况 - 但是我想它并不那么相关。

这有助于稍微解释一下,但问题 #5610 是什么?也许这会告诉我们。在 GitHub 存储库上看不到问题跟踪器?jQuery 有自己的问题跟踪器,如果我们在那里搜索#5610(或在 Google 上搜索)jquery issue 5610,我们最终会找到问题 #5610


MAKEARRAY 可能导致浏览器在具有长度属性的非数组对象上崩溃

描述

我还没有用 1.4 测试过这个

对于具有转换为非零正整数的长度属性的对象,makeArray 将创建一个长度为未定义的数组。

>>> jQuery.makeArray({'length': '5'})
[undefined, undefined, undefined, undefined, undefined]

如果长度属性为零、负数或小数,则 Firefox 和 Safari 都会挂起。(FF 显示无响应的脚本错误,Safari 挂起直到崩溃)

>>> jQuery.makeArray({'length': '0'})
>>> jQuery.makeArray({'length': '5.2'})
>>> jQuery.makeArray({'length': '-3'})
于 2013-08-02T22:18:34.173 回答
0

您可以将对象传递给函数,例如

merge ({ length: 'mylength' }, { length: 'mylength' });
于 2013-08-02T18:35:11.713 回答
0

如果 second 未定义或为 null 或对象不支持长度,则 l 将不是数字。我不能说为什么 second 可能是 undefined 或 null 因为它是从调用者传递的。

于 2013-08-02T18:44:18.957 回答