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

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

我通过研究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 (ie 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, eg 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已被包含在包装插件的匿名函数的参数中(我不知道还有什么可以称之为)因为它在注释中给出了它有点缩短了执行时间。 但是这有什么作用呢? 包装插件的所述匿名函数的任何参数都会被传递到哪里? 如何在插件中解决这些问题?

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

这让我想到了另一个问题:什么是未定义的 ? 它不是分配给未在范围中定义的对象数据类型吗? 怎么能作为一个论点传递? 参数不必是对象吗? 在评论中有几行关于这一点,但我不明白它的一句话:< 所以我们可以确保它的价值真的未定义 > whaaa?

总结一下:

  • function($)的确含义是什么?
  • 为什么我要将windowdocumentundefined作为function($)参数包含在内?
  • 如果我这样做,我如何访问实际windowdocument对象?
  • undefined什么,为什么?

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

当你写一个像这样的函数:

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

然后立即使用参数document调用该函数,并为参数foobar调用"myElement" 。 因此,在函数内部, 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的值。 您总是可以使用表达式void 0 获取undefined的真值:如myUndefinedVar = void 0;

function($)是什么意思?

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

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

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

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

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

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

此外,相同的性能原因适用于undefined

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

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

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

现在进入细节:

function的确是什么意思($)

没什么特别的。 在javascript函数和变量名中允许使用字母az包括大写,数字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!"的价值"boom!" 。 javascript语言的最新规范实际上不允许重新分配到未定义但是到今天只有Safari这样做。

再一次,没有理智的程序员会这样做。

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

 function bar(foo) { } 

相当于

 function bar(foo) { var foo; } 

但是,如果你想将值传递给foo,你当然会先做第一个。

做的主要原因:

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

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

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

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

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

现在您确定函数范围中未定义的内容确实未定义。 或者如果你想要一个任务:

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

但这只是额外的打字。