JavaScript 和 jQuery 中的 Java 式 OOP 失败

Posted

技术标签:

【中文标题】JavaScript 和 jQuery 中的 Java 式 OOP 失败【英文标题】:Java-esque OOP in JavaScript and a jQuery fail 【发布时间】:2011-07-25 14:00:17 【问题描述】:

我正在做一个项目,我真的在尝试编写面向对象的 javascript 代码。我刚刚开始阅读Douglas Crockford's JavaScript: The Good Parts,我很快就开始意识到用 JavaScript 编写 Java 式 OOP 将是一项艰巨的任务。

到目前为止,我已经写了类似以下的内容...

// index.html
$(document).ready(function() 

   $().SetUpElements();
);

// this is in a different js file
$.fn.SetUpElements = function() 

    // do stuff here
    $().UpdateElement();
;

// this is in yet another different js file
$.fn.UpdateElement = function() 

   // do stuff here
   var element = new Element(id, name); // continue doing work
;

function Element(id, name) 

   var id = id;
   var name = name;

   // other stuff
;

... 我希望对象/函数尽可能地重构和解耦;我想尽可能多地重用代码。我将很多代码分布在不同的 .js 文件中,目的是将特定的相关代码组合在一起,就像用 Java 编写不同的类一样。

随着我对 jQuery 的了解越来越多,我意识到符号 $.fn.foo = function() ... ; 实际上是将这个 foo 函数添加到所有 jQuery 对象的原型中。这是我应该做的事情吗?我是否以某种方式滥用了 jQuery?

我会很感激关于如何改进我在 JavaScript 中的 OOP 方法的建议,我很想看到对讨论这个主题的资源/教程/文章/等的引用。即使选择了答案,也请随时提供反馈。我正在寻找你的建议......这就是我发布的原因:)

** 注意:我不是在开发 jQuery 插件。我正在开发一个网络应用程序并大量使用 jQuery。

【问题讨论】:

查看 require.js 以处理中大型项目的文件加载和依赖关系。 @Raynos... 肯定会看看。是不是类似于headjs.com 【参考方案1】:

如果不是 jquery 插件,我会这样写:

    var app = 
      init: function()
        app.SetUpElements();
      ,
      SetUpElements: function() 
        return 'something';
      , 
      etc: function() 

      
    ;
   $(document).ready(app.init);

【讨论】:

@minibikini...变量app代表什么? 这就是@jamietre 所说的“设置你自己的命名空间”的意思。 app 是您的命名空间全局对象,它将包含您的分组函数和属性。 app 应该是一个非常独特的应用程序名称,因为它是您的主要全局对象。 现在把它包起来!这样您就不会用app 污染全局范围【参考方案2】:

关于在 Javascript 中做 OO 你应该看Douglas Crockford's Advanced Javascript lessons

【讨论】:

@mlaw...我实际上开始阅读他的书“JavaScript - The Good Parts”。我一定会看看课程。谢谢! 这些视频比书提供的信息要多得多。尽管我将强制引用另一本书 - JavaScript:权威指南 (amazon.com/JavaScript-Definitive-Guide-David-Flanagan/dp/…)。【参考方案3】:

我会说您创建方法的第一种方式是滥用 jQuery。 jQuery.fn.foo 语法通常保留给作用于 DOM 元素的函数,但您通过使用空的 jQuery 对象将它们用作静态函数。

如果你想在 jQuery 命名空间下创建静态函数,你可以这样做:

jQuery.foo = function();

然后通过以下方式调用它:

jQuery.foo();

代替:

jQuery.fn.foo = function();

它允许你这样做:

jQuery('#someElementId').foo();

在 OOP 方面。有许多不同的方法(模块模式、原型、工厂......)。我通常处理它的方式是创建一个类作为静态函数,然后使用关键字new 调用它

(function($)
    var undefined;

    $.ClassName = function(options)
        var self = this;
        var cfg = $.extend(true, , this.defaults, options);

        // ********************
        // start:private
        // ********************
        function _init()

        ;

        // ********************
        // start:public
        // ********************
        this.methodName = function()

        ;

        _init();
    ;

    $.ClassName.prototype.defaults = ;
)(jQuery);

在重用功能方面,有一个阈值,在此阈值之后,解耦比任何事情都更有害。确保在模块化和组织之间保持适当的平衡。

【讨论】:

@alex... 这看起来不错,谢谢!您写的内容与其他用户建议的“命名空间”想法有何不同? @Hristo 好吧,我实际上只是将它放入 jQuery 命名空间中。这里的大多数人都反对它,所以我的代码可以很容易地更改为 YOURNAMESPACE.ClassName = function() @alex... 哦,好吧,我明白了。 另外,@Hristo,我在这里使用的模式还有待商榷。有很多选择,请选择最适合您的情况的一种。我并不总是使用上述格式,只是大部分时间。例如,如果我计划拥有该类的许多实例,则出于性能/内存原因,我会将方法分解到类原型中。 @alex...感谢您的解释。一旦我恢复我的投票特权+1 :)【参考方案4】:

如果您正在开发插件,通常最好尽量避免污染 jQuery 和全局命名空间/原型。

看看this documentation on jQuery's website,它很好地解释了如何提供对各种可重用功能的访问,同时只在 jQuery 对象上声明一个名称。

我看到使用的另一种方法是在 jQuery 命名空间上为您的插件提供单个 init 函数,然后在全局命名空间中提供额外的插件特定名称,这提供了对函数的更直接访问/成员等。Galleria plug-in 是一个很好的例子,说明了如何做好这件事。检查他们的文档,以及他们设置插件结构的方式。

编辑

在看到你不是制作插件之后:我所说的许多东西仍然适用于一般的 jQuery 开发(实际上,适用于一般的开发)。

在您的特定情况下,由于您在进行函数调用时没有使用 jQuery 选择器,因此这些函数可能很容易附加到您自己制作的原型上,该原型可以放置在全局命名空间中。

就命名空间而言,请查看有关此问题的公认答案,以了解如何正确执行此操作的示例:How do I declare a namespace in JavaScript?

这里是 JavaScript 中 OO 的一个很好的资源。我在尝试理解这个概念时使用了这篇文章:http://mckoss.com/jscript/object.htm

【讨论】:

@Ender... 1. 我喜欢你的名字 :) 2. 我不是在开发插件。 @Ender...感谢“编辑”。这就是我想要前进的方向。现在,我已经两次看到对“命名空间”的引用......那是什么?我如何/在哪里可以了解有关创建自己的命名空间的更多信息? 我刚刚添加了对另一个 SO 问题的引用,希望能回答这个问题 :) @Ender... 太棒了,谢谢!只要我能恢复我的投票能力,我就会给你 +1。 @Hristo - 谢谢,很高兴我能帮上忙 :) 如果您需要进一步阅读,请尝试一些有关 javascript 原型的谷歌搜索。有大量资源解释了如何做(以及为什么)。【参考方案5】:

看起来您将所有代码都编写为 jQuery 插件?我不会那样做的。创建您自己的命名空间。

将所有内容放在不同的文件中的目的是什么?除非它们是用于特定页面的脚本,否则只会让管理变得更加困难。这些看起来都像一般的库函数。除非你认为你想在一个页面上加载一些,但不是全部,否则将它们全部放在一个文件中。

面向对象的编程并不意味着跨文件分割东西。这只是组织,当您不需要某个页面上的所有内容时,应该将内容分成不同的文件。

【讨论】:

@jamietre...嗯...这很有意义!我不是想写一个插件,所以我知道我的错误在哪里。我将内容分散到文件中的原因是我可以将特定功能组合在一起并轻松找到它,这样我就不必滚动浏览一个巨大的文件。 “创建自己的命名空间”是什么意思?你能提供解释/来源/链接/等吗?【参考方案6】:

正如大多数人已经提到的,除非您明确创建公共插件,否则不要使用 jQuery 自定义函数。而是使用简单的对象表示法或像Module Pattern 这样更健壮的东西来创建自己的命名空间。如果您想变得疯狂,您可以在 API 之上使用 API,通过使用 Dean Edward's Base 来获得更经典的 OO 模式。最终目标是从 jQuery API 中提取您的功能和设计需求。这样一来,如果您需要将某些东西移植到 Mootools 或 Prototype 上,完成起来会容易得多。

【讨论】:

@hal10001...你能提供更多关于创建命名空间的信息吗?这实际上是我第一次听说这个词:/

以上是关于JavaScript 和 jQuery 中的 Java 式 OOP 失败的主要内容,如果未能解决你的问题,请参考以下文章

JQuery的开发与使用心得

如何将jquery转换为javascript

JavaScript相关

jQuery是什么?

跨浏览器窗口大小调整事件 - JavaScript / jQuery

从 javascript 数组中删除元素的干净方法(使用 jQuery、coffeescript)