使用'window','document'和'undefined'作为包装jQuery插件的匿名函数中的参数

时间:2013-04-03 01:48:19

标签: javascript jquery jquery-plugins boilerplate dollar-sign

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

我通过研究SlidesJS插件的来源学习了如何编写jQuery插件。当我遇到新的东西时,我只是问了我的好朋友 Google ,并且大部分时间都得到了满意的答案。老实说,我从来没有做过多少努力。我所知道的是$(可能)是一个简写的jQuery对象构造函数,如果包含jQuery,$()jQuery()是相同的。

最近,我试图理解jQuery背后的科学以及如何编写一个好的 jQuery插件。我遇到了一个非常好的article,其中作者列出了几个用于创建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 );

我已将评论纳入其中,以便在我的问题中引用它们。

我有一个粗略的想法,为什么windowdocument被包含在包装插件的匿名函数的参数中(我不知道还有什么叫它)< / em>因为它在注释中给出了它有点缩短了执行时间。但是这有什么作用呢?包装插件的所述匿名函数的任何参数都传递到哪里?如何在插件中解决这些问题?

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

这让我想到了另一个问题:什么是未定义?是不是数据类型分配给范围内未定义的对象?怎么能作为一个论点传递?参数不必是对象吗? 中有一些关于此的内容,但我不明白:&lt; 所以我们可以确保它的值真正未定义&gt; whaaa?

总结一下:

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

请放轻松我。我从未将编程语言作为编写应用程序的明确目的而学习。我研究了基本的C,用于为微型核心微控制器编写面向硬件的低级例程,而这就是它。我确实学习了很多C ++,并且自己学习了一些Java。这样你就会知道会发生什么。

4 个答案:

答案 0 :(得分:23)

当您编写如下函数时:

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

然后立即使用参数document"myElement"调用函数,以获取参数foobar。因此,在函数内部,foo.getElementById(bar)相当于document.getElementById("myElement")

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

  

function($)究竟是什么意思?

$只表示对传递给包装函数的jQuery对象的引用。稍后,当使用(jQuery, window, document)调用匿名函数时,函数内的$引用引用jQuery对象。这样做有很多原因,尤其是$可以更快地输入。它还允许用户将包装器中的插件应用于jQuery特定实例,可能由jQuery.noConflict()生成。

  

为什么我应该将windowdocumentundefined作为function($)的参数包含在内?

您不需要来包含这些内容。原作者的推理是,分配函数局部变量来引用它们将缩短解决这些变量所需的时间。我断言节省的费用可以忽略不计;除非我使用很多window和/或document的引用,否则我个人不会打扰。

至于undefined,原作者的目的是确保有人没有改变EcmaScript 4中的undefined全局变量(编辑:实际上ECMAScript 3 - 版本4从未制作过它)或更早。再次,我无法想象这个问题出现了。如果您真的担心这可能是一个问题,请在您的函数中包含类似的内容:

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

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

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

  

undefined什么,为什么?

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

答案 1 :(得分:3)

  

function($)是什么意思?

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

  

为什么我应该将windowdocumentundefined作为function($)的参数包含在内?   我有一个粗略的想法为什么窗口和文档已被包含在包装插件的匿名函数的参数中(我不知道还有什么可以调用它),因为它在注释中给出了它有点缩短了执行时间。但是这有什么作用呢?包装插件的所述匿名函数的任何参数都传递到哪里?

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

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

您传递undefined的原因是因为undefined是一个令人困惑的混乱。基本上,local variables are first in the chain,即它是一个变量,一个单例它不受保护,这意味着可以覆盖。这意味着您可以拥有实际定义的undefined

通过传递undefined,您确保即使有人在全局范围内与undefined混淆(从不信任全局范围!:)),您仍然可以得到正确的undefined

此外,相同的效果原因适用于undefined

  

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

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

答案 2 :(得分:2)

在回答详细信息之前,请注意,与其他编程语言不同,javascript有点奇怪,原因有二:首先,它是在急速创建的,这意味着很多东西没有得到改进或实现得很好。其次,它非常非常快地被互联网采用,微软非常快速且非常准确地复制了语言(包括语言中的错误)。结果是,尝试纠正语言设计中的错误很难和/或不可能,因为他们不想破坏与现有网站的兼容性。

现在进入细节:

函数($)

的确含义

没什么特别的。在javascript函数和变量名称中,允许使用字母a-z,包括大写,数字0-9和符号$_。对它们的使用方式没有其他限制。虽然有一些指导方针和惯例,有些是由语言规范本身提到的,但有些已经随着编程社区的发展而增长。

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

function foo ($) {alert($)}

function foo (x) {alert(x)}

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

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

为什么我要将window,document和undefined包含为函数($)的参数?

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

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

window = 42;

没有理智的程序员会做到这一点,但它可能也是如此。 jQuery开发人员非常关注这一点,所以jQuery会尽力将真正的windowdocumentundefined传递给插件,以防任何人疯狂到做疯狂的事情。

javascript的一个特性是函数参数覆盖全局变量。因此,如果上述任何变量被其他人劫持,则会将其重新分配给函数中的正确名称。

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

您需要将正确的windowdocumentundefined作为名为windowdocumentundefined的参数传递到函数中。做其他事情意味着您无法再访问windowdocumentundefined个对象。

你可以做一些疯狂的解决方法,试着抓住window对象(也称为全局对象),然后你可以抓住document。 jQuery代码实际上是一个解决方法,以便在它被劫持时返回undefined

未定义什么,为什么?

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

undefined = 'boom!';

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

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

答案 3 :(得分:0)

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

function bar(foo) {
}

相当于

function bar(foo) {
  var foo;
}

但当然如果你想将值传递给 foo ,你只需要做第一个。

主要原因:

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

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

传入文档窗口充其量只是微优化,几乎没有什么好处。它没有提供保护,不会为它们分配不同于预期的值。只是不要打扰并使用函数内的 window document 作为全局标识符。

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

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

现在你肯定,函数范围内的 undefined 确实是未定义的。或者如果你想要一个任务:

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

但这只是额外打字。