什么是这个代码构造包装一个库的一部分,它有什么用处?

我模仿了一个库,并能够写下面的代码。 这段代码创build了'c'对象,其中分配了'a'function。 所以,要调用'a' ,我将不得不写ca()

此外,我能够添加更多的function,这个'c'对象。 我想了解这个代码中发生了什么。 它看起来不像普通的面向对象编程。 什么是这种types的JavaScript编程称为?

 var c = (function(c) { if (c === undefined) { c = {}; } function a() { alert(1); } ca = a; return c; }(c)); 

这是一个模块模式。 你会看到这种模式的许多变种,所以了解真正发生的事情是很重要的,你不能模仿一个模式。

这段代码的重点是完成一个对象c (通常是你的全局库)。 您的应用程序中可能有许多类似的代码片断,所有这些代码片断都可能构build成c ,可能每个都在自己的文件中。

如果库对象c (作为parameter passing给函数)尚不存在( c === undefined ),则会创build它。 这使得不依赖于执行顺序或预先存储的文件成为可能。

分配的右边部分是立即调用的函数IIFE (即时调用函数expression式)。 这种结构的优点是它创build了一个范围,可以在其中声明variables(例如a函数)而不会污染外部(全局)范围。 在这里,重点是无论如何都是外部的,但是模块通常依赖于几个内部(私有)function和variables。

一个细节可能需要一个解释:所有这些文件看起来像他们定义一个新的variablesc但这里没有问题,即使文件连接:一个var语句不定义一个新的variables,如果它已经存在(variables是在整个范围内定义,在全球范围内,甚至在声明之前)。

另一种方式来写这个会是

 var c = c || {}; // ensure the c variable is defined, and initialize its value it if necessary (function() { // let's use an IIFE to have a protected scope and not pollute the global one function a() { alert(1); } ca = a; // let's augment c })(); 

这一个可能更清楚

  • 它明确地分离了两个步骤( c初始化和使用IIFE的c完成)
  • 它不依赖于具有相同名称的两个cvariables
  • 它不那么冗长

这里是相同的代码,每条线都做了更多的评论,当我们通过它时会发生什么。

 //Here, we're defining a function that will return an object. //its only parameter is named 'c' //this is confusing, since the parameter has the same name as the global definition of the function. //the var c definition is the global definition. the function parameter is not. //so every reference to anything named 'c' inside the function definition is local. var c = (function(c) { //if c (the c we passed as a parameter) is not yet defined if (c === undefined) { //define c as an object c = {}; } //define a function function a() { alert(1); } //attach it to the object ca = a; //return the object return c; }(c)); // call the constructor we just defined, using the global definition of `c`. // the first time we pass this point, c as a variable is still undefined. 
 var c = (function(c) { if (c === undefined) { c = {}; } function a() { alert(1); } ca = a; return c; }(c)); 

让我们一步一步来。

var c =

初始化一个名为c的variables。 请注意,在这一点上,如果一个名为c的variables已经被初始化,那么只有在我们到达这个声明的结尾时, c才会引用这个值。

( .... )

这意味着无论在里面什么都应该被视为expression。

function(c)

意味着这个“expression”是一个需要参数的函数。 这个论点从此将被称为c直到函数结束。 因此,在函数范围之外声明的名称为c任何variables都不能直接在这里访问。 虽然如果它在全局范围内,并且全局范围恰好是窗口对象,它可以被称为window.c

if (c === undefined) { ... }

检查传递给它的参数是否未定义。 如果未定义,将返回true,从而执行if块中的任何内容。

c = {}

将variablesc设置为空对象。 所以如果这个参数是(传递或者是未定义的),我们在这里自己定义(我们把它定义为一个空对象)。

function a() { alert(1); }

声明一个函数名称为a调用将导致提醒数字1.请注意,这只是一个函数声明。 我们还没有调用这个function。

ca = a

参数c现在被分配一个名为a的属性,它指向我们刚刚创build的函数。

return c

在经历了对传递的参数所做的更改之后,用返回值作为c的最终值来跳出函数。

(fun...}(c))

调用我们刚刚创build的函数,并将其作为parameter passing给c的当前值。 因为我们把函数作为expression式来调用,这个expression式的结果就是函数的返回值。 而且我们的函数在给它分配一个属性之后返回一个对象(我们传给它)。

由于该expression式等同于variablesc ,因此expression式的返回值(即函数的返回值)现在保存在variablesc

如果你正确地阅读了这一切,你就会知道variablesc现在拥有一个对象,该对象将拥有一个属性a ,该属性是一个警告数字1的函数。该对象还保留了之前可能拥有的属性。

如果我们通过使variables名称具有描述性来解密这个代码来使其可读:

 var myObject = (function(someObject) { if (someObject === undefined) { someObject = {}; } function alertOne () { alert(1); } someObject.alertOne = alertOne; return someObject; }(myObject)); 

这个节目是系列模块揭示模式中的一个插曲,它告诉我们如何在不污染全局范围的情况下以优雅的方式向之前声明的对象添加附加属性。 主演立即调用函数expression式(IIFE)。

为什么最后我们又写了(c))?

这被命名为立即调用函数

 (function(received argument){ //some code here....... })(passed argument) //<-- here the function is invoked 

c是传递的参数。 该函数在创build时立即被调用。 这种types的声明用于保持variables不变,并保持全局名称空间的清洁。

代码中的模式用于创build模块 。

…………现在来找你的代码:…………

如果传递参数未定义:………….

首先, ...}(c))立即调用函数的这一部分被调用。它被传递一个名为c的参数,它尚未定义。 (function(c){...部分接收这个c参数。

这里首先passed argument c是undefined.So if(c==undefined)被触发。在这一点上使用c={}语句,未定义的对象c被分配一个empty object

这里function a() { //... }是一个在模块内创build的私有方法,不能全局访问。

私有方法 a是通过使用ca=a语句将其分配给c而成为全局可用的 。因此,当对象将返回时,您可以在全局上下文中调用此方法。

因此新创build的空对象c被分配一个称为a的方法。然后返回并且var c接收这个对象。

如果传递的参数不是未定义的:…………

但是,如果passed c not undefined话,如果一个对象被传递,那么没有新的对象被创build。我的意思是if(c==undefined)是falsy。所以它不被执行。我的意思是没有新的空对象被创build。然后Passed对象被分配一个新的方法,称为使用ca=a

这很简单。

下面的代码是更简单的代码版本。 如果它最初是未定义的,它会自动发送一个空的对象,所以你不必费心检查是否不确定。它被称为松散的扩充。

 var c = (function(c) { function a() { alert(1); } ca = a; return c; }(c || {} )); 

Ben Cherry写的这篇文章帮助我理解了这种模式: http : //www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html

摘自文章:

匿名closures

这是使这一切成为可能的基本构造,并且实际上是JavaScript的单一最佳function。 我们将简单地创build一个匿名函数,并立即执行它。 所有在函数内部运行的代码都在一个闭包中,它在应用程序的整个生命周期中提供了隐私和状态。

 (function () { // ... all vars and functions are in this scope only // still maintains access to all globals }()); 

注意()周围的匿名函数。 这是语言所要求的,因为以token函数开头的语句总是被认为是函数声明。 包括()会改为创build一个函数expression式。

全球import

JavaScript有一个被称为隐含全局的特性。 无论什么时候使用名字,解释者都会向后search范围链,寻找这个名字的var语句。 如果没有find,则认为该variables是全局的。 如果它在一个赋值中被使用,则全局被创build,如果它不存在的话。 这意味着在匿名闭包中使用或创build全局variables很容易。 不幸的是,这导致难以pipe理的代码,因为它对于给定文件中的哪些variables是全局的并不明显。

幸运的是,我们的匿名函数提供了一个简单的select。 通过将全局variables作为parameter passing给我们的匿名函数,我们将它们导入到我们的代码中,这个代码比隐含的全局variables更加清晰和快速。 这是一个例子:

 (function ($, YAHOO) { // now have access to globals jQuery (as $) and YAHOO in this code }(jQuery, YAHOO)); 

模块导出

有时你不仅仅想使用全局variables,而是要声明它们。 我们可以通过使用匿名函数的返回值导出它们来轻松完成此操作。 这样做将完成基本的模块模式,所以这里是一个完整的例子:

 var MODULE = (function () { var my = {}, privateVariable = 1; function privateMethod() { // ... } my.moduleProperty = 1; my.moduleMethod = function () { // ... }; return my; }()); 

请注意,我们已经声明了一个名为MODULE的全局模块,它有两个公共属性:一个名为MODULE.moduleMethod的方法和一个名为MODULE.moduleProperty的variables。 另外,它使用匿名函数的closures维护私有的内部状态。 而且,我们可以使用上面学习的模式轻松导入所需的全局variables

你正在做的是声明一个匿名函数,然后用一个叫做c的参数来调用它,并把它赋值给一个variables,也就是c ,这个variables很混乱:-)

重命名variables这是你有:

 var result=(function (input_parameter){...} (parameter_used_to_call_my_function)); 

最后(c)你问的是调用函数的参数。 如果使用更长的语法,则更容易看出:

 var my_function=function(input_parameter){...}; var result=my_function(result); 

还值得一提的是,你正在调用my_function使用result (尽pipe你称为c )作为参数,这也是你刚刚创build的variables的名称来存储函数的返回值。 JS和这一点是一致的,因为它不是如何处理variables的严格,但这是一个令人困惑的方式来编写代码。 你正在声明一个variables,并把它作为parameter passing给你的函数,以防它早些时候被声明(并且在函数内部处理这种情况,至less是一致的)。

my_function里面发生的是你检查的是你的参数是否有一个以前的值(如果是前面的段落,我会这么解释); 如果未定义,则将其初始化为空对象。 然后你将一个函数附加到input_parameter并返回它。

我不知道这种types的编程是否有名称,但是对于不同的东西使用相同的variables名称并不是一个好主意:-)