自引用匿名闭包:JavaScript是否不完整?


18

匿名自引用函数闭包在JavaScript中如此盛行的事实是否表明JavaScript是不完整的规范?我们看到了这么多:

(function () { /* do cool stuff */ })();

并且我想一切都取决于品味,但是当您只想要一个私有名称空间时,这看起来不像是垃圾吗?JavaScript无法实现包和适当的类吗?

与同样基于ECMAScript的ActionScript 3相比,您可以获得

package com.tomauger {
  import bar;
  class Foo {
     public function Foo(){
       // etc...
     }

     public function show(){
       // show stuff
     }

     public function hide(){
       // hide stuff
     }
     // etc...
  }
}

与我们在JavaScript中执行的卷积相反(这来自jQuery插件编写文档):

(function( $ ){

  var methods = {
    init : function( options ) { // THIS },
    show : function( ) { // IS   },
    hide : function( ) { // GOOD },
    update : function( content ) { // !!! }
  };

  $.fn.tooltip = function( method ) {

    // Method calling logic
    if ( methods[method] ) {
      return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof method === 'object' || ! method ) {
      return methods.init.apply( this, arguments );
    } else {
      $.error( 'Method ' +  method + ' does not exist on jQuery.tooltip' );
    }    

  };

})( jQuery );

我很高兴知道这个问题很容易演变成关于首选项和编程风格的咆哮,但我真的很想知道您经验丰富的程序员对此有何感觉以及它是否自然,例如学习新语言的不同特质或糊涂,就像一些尚未实现的基本编程语言组件的解决方法?


22
“ JavaScript无法实现……适当的类吗?” 不。它已经有合适的原型。原型并不逊色于类。他们是不同的。人们尝试过在不同时间向JavaScript添加类,但都没有成功。
Rein Henrichs

5
@Rein:但是以某种方式由ActionScript进行管理...
Mason Wheeler

8
@Tom没有“内置类”。根本没有原型语言的课堂。您一直在混淆这两种范例。
Rein Henrichs

1
我个人发现匿名函数语言功能比类更灵活。但是,我喜欢这种习惯用法很常见的函数式编程。
Dietbuddha

1
对于其他用户,这里:brianodell.net/?page_id=516是JavaScript作为一种原型语言的出色入门。
Tom Auger

Answers:


9

我想一切都取决于品味,但是当您想要的只是一个私有名称空间时,这看起来不像是垃圾吗?JavaScript无法实现包和适当的类吗?

大多数评论都与“原型是穷人的阶级”这一神话背道而驰,所以我将重复一遍,基于原型的OO在任何方面都不逊于基于类别的OO。

另一点是“在所有需要的地方都浪费一个私有名称空间”。如果您知道Scheme使用完全相同的合并定义范围,您可能会感到惊讶。这并没有成为做好词法作用域的典型例子。

当然,在Scheme中,“ kludge”隐藏在宏后面。


1
您没有证据支持Scheme是词法作用域做得很好的主要示例,或者与Scheme如何使用函数定义作用域有关。
DeadMG

不能说Scheme是一个典范,但JS联合创建者Brendan Eich的一个示例在这里讨论Scheme在JS的设计中发挥作用: readwrite.com/2011/07/22/javascript-was-no-accident
Erik Reppen

7

首先,有几件事:

  1. 查看JavaScript的另一种方式是使用函数作为构造体可以完成100万和1件事。如果您寻找它,一切就在那里。它只是离函数不远。

  2. 那个jQuery插件创作的东西太糟糕了。我不知道他们为什么这么提倡。$扩展名应该是通用的东西,而$已经包含了相当不错的内容,而不是构建我完整的小部件方法。这是DOM-API规范化工具。最好将其埋入您自己的对象中。我看不到将其用作完整的UI库存储库的吸引力。

客户端Web上的程序包毫无意义

我个人不喜欢客户端网络上的程序包,因为我们基本上是假装我们正在做一些我们实际上不是的事情。在发布.NET Web表单和令人发指的东西(从未从我们的Java朋友那里淘出来)的过程中,我宁愿将大量带有链接资源的HTML视为真正的东西并不要假装其他东西来安抚耐新事物的OS应用程序开发人员。在客户端Web上的JS中,没有任何东西被“导入”,除非对Ajax做一些糟糕的事情,而Ajax却忽视了浏览器缓存,是的,许多人都试图这样做。对浏览器而言,最重要的是它是否已被加载并被解释,或者没有被加载。我们不会在客户端上存放更多代码,以防万一。有充分的理由。#1是我刚刚描述了Web应用程序的插件和浏览器插件依赖性,因为这种现象通常无法很好地解决。我们现在要上网。这不是Adobe或Sun在本周第三次完成更新之后。

语言具有结构所需的内容

JS对象是高度可变的。我们可以在任何程度上拥有名称空间的分支树,因为我们认为这样做很有用,而且非常容易做到。但是,是的,对于任何可重复使用的东西,您都必须将任何库的根目录放在全局空间中。无论如何,所有依赖项都同时链接和加载,那么做其他事情有什么意义呢?避免全局命名空间的意义并不在于存在任何不良情况。太多的东西是不好的,因为冒着名称空间冲突或意外覆盖核心语言功能的风险。

只是因为它受欢迎并不意味着我们做对了

现在,当您在客户端Web应用程序中看到所有这些内容时:

(function(){
//lots of functions defined and fired and statement code here
})()

问题不是我们缺少构建应用程序的工具,而是人们没有重视结构。对于设计机构中的2-3页一次性一次性临时站点,我真的没有问题。当您必须构建一些可维护,易读且易于修改的东西时,它变得很丑陋。

但是,当您到达只需要实现所有可重复使用的对象和工厂的地方,并且可能会有一个或两个新的临时var进入该过程时,这是一种便利。

但是有带有包/模块的JS实现

请记住,在Node.js中,这样的事情更有意义,它们确实具有模块。假设我们可以避免困扰其他语言的uber-config-hell,那么JS是方程式中的唯一内容,每个执行的文件都是其自己的隔离范围。但是在网页上,链接js文件本身就是import语句。快速进行更多导入只是浪费时间和资源,因为获取资源需要付出更多的努力,而不是简单地将链接添加到文件(根据需要),因为如果另一个页面再次需要它们,它们将被缓存在浏览器中。因此,除了创建适配器对象工厂(如jQuery或更传统的对象,这些对象可以覆盖给定域中的很大一部分任务,同时占据全局的一个位置)以外,尝试通过做其他任何事情来扩展全局空间。那里'http://wiki.ecmascript.org/doku.php?id=harmony:modules

因此,不存在,当有充分的理由使用此类调用时(通常会使用),避免了全局命名空间污染的自动调用程序没有任何问题。而且我们在对象中具有持久的私有等效属性(只需在构造函数中定义var,而不要将其公开为属性)。

但是,我们可以做这样的事情真是太棒了。大量使用表明JS开发人员可能仍在成熟,但是对于任何不试图将范例推向客户端Web的人来说,这并不是一个巨大的漏洞。


向唐纳德投票,你能解释为什么吗?当有人写那么多书时,我认为他应该得到解释!
Songo 2013年

+1好答案,不确定为什么要投下反对票。
pllee

很棒的写作和广阔的视野。我也喜欢“仅仅因为它是单反而不是对。”。我认为我的问题是我对更严格的语言比较满意,这(IMO)在许多方面有助于提高开发效率。没有很多内置的制衡机制,JavaScript似乎真是一厢情愿:您可以做任何您想做的事情,因此,这里面有许多成语和习惯。很难确定“正确”的方法来接近您的编码结构。尽管我同意对于快速的一次性工作的人来说,这并不是一个大问题。
Tom Auger

1
IMO,除了吊死自己之外,还有很多事情可以用绳子做,并且您学会从偶尔的自挂起更快地编写健壮的代码,这种情况在您养成更好的习惯时很少发生,但我不会假装是为了每个人或每项工作的理想人选。我怀疑您对它了解的越多,但是发现它的容忍度就越高。当我尝试用没有一流功能或对象(如JS的灵活/可变对象)的语言来做事情时,我感觉好像失去了一半的大脑。
Erik Reppen

4

您缺少的另一件事是javscript必须向后兼容。如果尝试引入包语法,则可能会以某种疯狂的方式破坏网络。那就不好了!道格·克罗克福德(Doug Crockford)在各个方面都谈到了这一点,以及为何添加尝试失败的原因。


那是个很好的观点。然而,ActionScript通过简单地发布新版本来管理它。在定义脚本标签时,您始终可以指定JavaScript版本,因此“中断”现有站点应该不是问题。
汤姆·奥格

1
实际上,网络上的大多数脚本标签都没有版本号。老实说,我不确定这件事的所有问题,但我确实知道,考虑到这些问题的人们已经决定不可行。
Zachary K

1
@Tom:Adobe也完全控制Flash平台。没有一个实体可以完全控制那里的所有JS平台。而且,仅在浏览器中添加JS脚本的版本号就意味着您要么不支持较旧的浏览器,要么必须编写两个脚本。因此,这一个问题。
Jeremy Heiler

2

是的,这太过分了。

许多人在说“原型并不逊色于阶级”。我不同意,但这是优先事项。但这甚至不是JavaScript的真正问题-问题在于它最初被设计为一种用于制作动画按钮之类的快捷脚本语言。上世纪90年代中期,从来没有人想到过JavaScript会被要求做一些它现在正在做的疯狂事情。


6
我不同意,JavaScript语言实际上真的很棒。它与一个未指定且互不兼容的DOM混为一谈,这是所有问题的开始。
迪恩·哈丁

2
这与原型有什么关系?
Erik Reppen

2

匿名自调用函数更类似于模块而不是类。令人讨厌的是,javascript的默认值是在全局范围内运行。研究JS.next的委员会正在认真考虑添加模块,以免将局部变量放入全局范围。幸运的是,Javascript函数具有便捷的语义,因此我们可以相对轻松地将匿名函数用作私有范围。

我看不到类是如何真正进入讨论的,只是它们是许多语言中的顶级范围构造。更好的模块/软件包/请给我一个本地范围的代码,这样我就不会在全局环境中留下我的变量了。


1

您可能想看一下ExtJS 3和ExtJS 3,它们已经很好地实现了名称空间。

-在-1之后添加

我的意思是,可以隐藏所有这些“卷积”,并且仍然具有相当友好的代码,如下所示:

Ext.ns('com.tomauger');
Ext.Loader.load('bar.js'); //unfortunately filname needs to be used
MyNameSpace.Foo = {
   //...
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.