25

老实说,我不知道如何缩短标题。

通过学习SlidesJS插件的源码,我学会了如何编写一个 jQuery插件。当我遇到新事物时,我只是问了我的好朋友谷歌,大多数时候都得到了满意的答案。不过老实说,我从来没有努力过。我所知道的$是(可能)是一个简写的 jQuery 对象构造函数,$()并且jQuery()在包含 jQuery 的情况下也是一样的。

不过,最近,我试图了解 jQuery 背后的科学以及如何编写一个好的jQuery 插件。我遇到了一篇非常好的文章,其中作者列出了几个用于创建 jQuery 插件的模板。由于其余的太复杂了,我无法理解,我喜欢第一个:轻量级的开始。现在,这里是上述模板的代码。

/*!
 * jQuery lightweight plugin boilerplate
 * Original author: @ajpiano
 * Further changes, comments: @addyosmani
 * Licensed under the MIT license
 */


// the semi-colon before the function invocation is a safety 
// net against concatenated scripts and/or other plugins 
// that are not closed properly.
;(function ( $, window, document, undefined ) {

    // undefined is used here as the undefined global 
    // variable in ECMAScript 3 and is mutable (i.e. it can 
    // be changed by someone else). undefined isn't really 
    // being passed in so we can ensure that its value is 
    // truly undefined. In ES5, undefined can no longer be 
    // modified.

    // window and document are passed through as local 
    // variables rather than as globals, because this (slightly) 
    // quickens the resolution process and can be more 
    // efficiently minified (especially when both are 
    // regularly referenced in your plugin).

    // Create the defaults once
    var pluginName = 'defaultPluginName',
        defaults = {
            propertyName: "value"
        };

    // The actual plugin constructor
    function Plugin( element, options ) {
        this.element = element;

        // jQuery has an extend method that merges the 
        // contents of two or more objects, storing the 
        // result in the first object. The first object 
        // is generally empty because we don't want to alter 
        // the default options for future instances of the plugin
        this.options = $.extend( {}, defaults, options) ;

        this._defaults = defaults;
        this._name = pluginName;

        this.init();
    }

    Plugin.prototype.init = function () {
        // Place initialization logic here
        // You already have access to the DOM element and
        // the options via the instance, e.g. this.element 
        // and this.options
    };

    // A really lightweight plugin wrapper around the constructor, 
    // preventing against multiple instantiations
    $.fn[pluginName] = function ( options ) {
        return this.each(function () {
            if (!$.data(this, 'plugin_' + pluginName)) {
                $.data(this, 'plugin_' + pluginName, 
                new Plugin( this, options ));
            }
        });
    }

})( jQuery, window, document );

我已将评论包括在内,以便在我的问题中引用它们。

我有一个粗略的想法,为什么window并且document已经包含在包装插件的匿名函数的参数中(我不知道还能调用什么),因为它在评论中给出了它有点缩短了执行时间。但这是如何工作的?包装插件的上述匿名函数的任何参数都会传递到哪里?这些在插件中是如何解决的?

通常,我会这样做$(window).resize(function(){}),但在这种情况下不起作用。如果我console.log(window)在插件函数内部执行,它会显示“未定义”。

这让我想到了另一个问题:什么是未定义的?它不是分配给未在范围内定义的对象的数据类型吗?它如何作为参数传递?论点不是必须是对象吗?评论里写了几行关于这个,但我一个字都看不懂:<所以我们可以确保它的值是真正的未定义> whaaa?

总结一下:

  • 究竟是什么意思function($)
  • 为什么我应该包含window,documentundefined作为 的参数function($)
  • 如果我这样做,我如何访问实际windowdocument对象?
  • undefined什么,为什么?

请对我放轻松。我从来没有为了编写应用程序的明确目的而将编程语言作为一门学科。我学习了基本的 C 语言,用于为微型核心微控制器编写面向硬件的低级例程,仅此而已。我确实广泛地学习了 C++ 和一点点 Java。只是为了让你知道会发生什么。

4

4 回答 4

23

当您编写如下函数时:

(function (foo, bar) {
    return foo.getElementById(bar);
})(document, "myElement")

然后立即使用参数调用该函数,document"myElement"为参数foobar. 因此,在函数内部,foo.getElementById(bar)等价于document.getElementById("myElement").

同样,在您的插件示例中,您会立即使用 arguments 调用该函数jQuery, document, window

究竟是什么意思function($)

简单地表示对传递给包装函数的对象的$引用。jQuery稍后,当使用 调用匿名函数时,函数(jQuery, window, document)内部$的引用会引用该jQuery对象。这样做的原因有很多,其中最重要的是$打字速度更快。它还允许用户将包装器中的插件应用到可能jQueryjQuery.noConflict().

为什么我应该包含window,documentundefined作为 的参数function($)

不需要包括这些。原作者的推理是分配函数局部变量来引用这些将缩短解析这些变量所需的时间。我断言节省的费用微不足道;除非我大量引用window和/或document.

至于undefined,原作者包含此内容的目的是确保有人没有更改undefinedEcmaScript 4 中的全局变量(编辑:实际上是 ECMAScript 3 - 版本 4 从未实现过)或更早版本。同样,我无法想象会出现这个问题。如果您真的担心这可能是一个问题,只需在您的函数中包含以下内容:

if(typeof undefined !== "undefined") {
    undefined = void 0;
}

如果我这样做,我如何访问实际windowdocument对象?

您所要做的就是确保匿名函数末尾的函数调用传入实际的(jQuery、窗口、文档)参数。或者,不要在函数签名中包含windowand参数。document无论哪种方式,您都将引用实际对象,而不管间接级别如何。

undefined什么,为什么?

undefined是“未定义”类型的全局变量。尚未初始化的字段与未定义完全相等 (===)。它允许程序员区分一个故意为空的值和一个简单的未初始化的值。在 ECMAScript 5 及更高版本中,undefined是只读的。在此之前,其他代码可能会修改undefined. 您始终undefined可以使用表达式void 0...获得真实值,如myUndefinedVar = void 0;.

于 2013-04-03T02:24:10.770 回答
3

是什么意思function($)

作为参数传递$给您的函数使您可以使用jQuery速记对象,正如您清楚地指出的那样,它只是jQuery对象的别名(另请查看jQuery.noConflict())。

为什么我应该包含window,documentundefined作为 的参数function($)?我有一个粗略的想法,为什么 window 和 document 已包含在包装插件的匿名函数的参数中(我不知道还能调用什么),因为它在评论中给出了它有点缩短了执行时间. 但这是如何工作的?包装插件的上述匿名函数的任何参数都会传递到哪里?

传递windowdocument作为局部变量有点缩短执行时间,因为访问局部变量比访问全局变量更容易和更快,因为局部变量在链中是第一个。哪种回答你的第二个问题:参数在匿名函数范围内传递,这就是你创建匿名函数的全部意义:创建一个闭包。

这些参数在函数末尾传递,您可以在其中看到最后一个括号。闭包中的变量window指的是,global window因为您在最后传递了它。

你通过的原因undefined是因为undefined是一个 Javascript 混乱的混乱。基本上,undefined全局对象的一个​​属性,也就是说,它是一个变量,一个单例它不受保护,这意味着可以被覆盖。这意味着你可以有一个undefined实际定义的。

通过传递,undefined您可以确保,即使有人undefined在全局范围内搞砸了(永远不要相信全局范围!:)),您仍然会得到正确的undefined.

此外,同样的性能原因也适用于undefined.

如果我这样做,我如何访问实际的窗口和文档对象?

您已经在访问它们,因为您正在将它们传递给您的函数。因此,您可以操纵它们。

于 2013-04-03T02:18:00.580 回答
2

在详细回答之前,让我指出,与其他编程语言不同,javascript 有点奇怪,原因有两个:首先,它是仓促创建的,这意味着很多东西没有得到完善或实现。其次,它很快就被互联网采用了,微软非常快速、非常准确地复制了这种语言(包括语言中的错误)。这样做的结果是,试图纠正语言设计中的错误是困难的和/或不可能的,因为他们不想破坏与现有网站的兼容性。

现在进入细节:

function($) 的真正含义是什么

没什么特别的。在 javascript 中,函数和变量名允许使用a-z包括大写、数字0-9和符号在内的字母$_. 对于如何使用它们没有其他限制。尽管有一些指导方针和约定,有些是语言规范本身提到的,有些是随着编程社区的发展而发展起来的。

因此,$只是一个变量名。两者之间没有区别:

function foo ($) {alert($)}

function foo (x) {alert(x)}

它只是为参数选择的名称。但是,该规范强烈建议$不应由人类编写的代码使用。对于计算机生成的代码(例如咖啡脚本编译器)可以,但对于常规脚本则不行。另一方面,强烈建议在使用 jQuery 的脚本中$总是引用 jQuery 对象(顺便说一下,它也是一个函数,这在 javascript 中很好,因为函数是对象)。

由于您正在编写 jQuery,因此 的含义function ($)是一个接受一个参数的匿名函数,它期望的参数是一个 jQuery 对象。

为什么我应该包含窗口、文档和未定义作为函数($)的参数?

可以说,javascript 中的设计错误之一是缺乏对常量和/或不可变/受保护的变量/对象的支持。因此windowdocumentundefined实际上是常规的全局变量 - 任何人都可以将它们重新分配给任何东西。

以下是疯狂但有效的 javascript 代码:

window = 42;

没有理智的程序员会这样做,但仍然有可能。jQuery 开发人员对此非常关注,因此 jQuery 尽最大努力将真实的window,document和插件传递给undefined插件,以防有人疯狂地做疯狂的事情。

javascript 的功能之一是函数参数覆盖全局变量。因此,如果上述任何变量已被其他人劫持,它们将在函数中重新分配给它们的专有名称。

如果我这样做,我如何访问实际的窗口和文档对象?

您应该将正确的window,documentundefined作为名为window,document的参数传递undefined给函数。做任何其他事情意味着您不再有权访问window,documentundefined对象。

您可以采取一些疯狂的解决方法来尝试抓住window对象(也称为全局对象),然后您可以从那里获得document. undefinedjQuery 代码实际上是在被劫持的情况下找回的一种解决方法。

未定义什么,为什么?

正如你所说的那样。undefined是 javascript 赋予已声明但未分配值的事物的值。但在 javascriptundefined中只是一个全局变量。如果你不碰它,它最初的值是undefined(循环逻辑,我知道)。但是可以修改:

undefined = 'boom!';

此后所有未定义的变量都将具有 的值"boom!"。javascript 语言的最新规范实际上不允许重新分配给 undefined 但截至今天只有 Safari 这样做。

同样,没有理智的程序员会这样做。

于 2013-04-03T02:30:28.437 回答
0

在参数列表中包含标识符实际上与在函数体中声明变量相同,例如

function bar(foo) {
}

相当于

function bar(foo) {
  var foo;
}

但是当然,如​​果您想将值传递给foo ,您只需执行第一个。

这样做的主要原因:

(function($) {
    // use $ here instead of jQuery
}(jQuery));

就是在 jQuery 发布时,Prototype.js 已经使用“$”作为其 main 函数的标识符已有一段时间了。上面的模式允许在同一个页面中使用 jQuery 和prototype.js,使用“$”作为不同事物的标识符。

传入documentwindow充其量只是一个没有什么好处的微优化。它不能防止它们被赋予与预期不同的值。只是不要打扰并使用函数内部的窗口文档作为全局标识符。

在参数中包含undefined并且不向其传递值并不是确保undefined确实是未定义的明智方法,因为如果意外传递了值,它仍然会受到影响。更安全的方法是:

(function() {
    var undefined;
    ...
}());

现在您确定函数范围内的未定义确实是未定义的或者,如果您想要分配:

(function() {
    var undefined = void 0;
    ...
}());

但这只是额外的打字。

于 2013-04-03T02:36:19.200 回答