【问题标题】:Forward declaration in JavascriptJavascript中的前向声明
【发布时间】:2011-11-20 03:49:55
【问题描述】:

背景

我正在构建一个基于 javascript 的应用程序,该应用程序在移动设备和桌面设备上的工作方式不同。但是,除了 DOM 操作之外,大多数代码在两个平台之间都是通用的,因此我将所有文件结构化,例如: * foo.core.js * foo.mobile.js * foo.web.js

并希望利用面向对象的技术来编写更简洁的代码。

问题:

我有两个带有类的 JavaScript 文件

文件 1:

function ClassA()
{}

ClassA.prototype.foo = function(){};

GreatGrandChildA.prototype = new GrandChildA(); // this is where the error is
function GreatGrandChildA ()
{}

文件 2:

ChildA.prototype = new ClassA();
function ChildA () // ChildA inherits ClassA
{}

GrandChildA.prototype = new ChildA()
function GrandChildA () // GrandChildA inherits ClassA
{}

通常,在像 C++ 这样的语言中,我会在文件 1 中转发声明 GrandChildA。我想知道如何在 Javascript 中做到这一点

编辑:

如果我创建一个包含所有四个类的单个文件 - 按照加载它们的相同顺序,该示例将完全按预期工作:

http://jsfiddle.net/k2XKL/

【问题讨论】:

  • 让我直截了当地说 - 您想在声明 GrandChildA 类的新实例之前分配它吗? Javascript 本身不支持任何方式来做到这一点。
  • 使用纯 javascript(没有帮助库)时,您必须先下载包含 parent 类定义的 js-file,然后再下载包含子类定义的 js-file。对于您的示例:必须在 File1 之前下载 File2。或者,您可以将两个文件合并为一个。如果您需要在父类之前下载子类,您可以使用外部库或实现您的自定义逻辑来下载和创建类。
  • @WTK:如果我将所有内容都放在一个文件中,效果会很好。不幸的是,我设计它的方式阻止了将所有内容放在一个文件中。但现在看来我必须重新构建整个应用程序。
  • @Andrew - 问题是脚本一个接一个地执行。所以无论哪个代码先执行,它都会失败。有趣的是,将两个脚本放在同一个文件中解决了这个问题。但那是我做不到的事情
  • @skyronic 是的,您可能需要重新构建代码组织。并阅读有关在 Javascript 运行时中提升以了解您的文件 2 实际工作的原因

标签: javascript oop javascript-objects


【解决方案1】:

我假设在您的 HTML 页面上,您先导入文件 1,然后再导入文件 2。

文件 1 中,您应该会看到异常,因为“GrandChildA”未定义。函数声明未完成,因为文件 2 尚未加载。

文件 2 中,您可以:

ChildA.prototype = new ClassA();
function ChildA () // ChildA inherits ClassA
{}

因为 Javacript 运行时提升了您的命名函数“ClassA”之前代码执行到ChildA.prototype = new ClassA();

请在http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting阅读有关函数提升的更多信息以及您是否应该在这种情况下进行操作

【讨论】:

    【解决方案2】:

    实现您想要的最明智的方法是制作 2 个单独的源代码版本。您将想要缩小、混淆您的代码并合并所有源文件,因此创建一个构建脚本(python 将是一个简单的构建脚本的好语言)配置为合并特定于移动设备是有意义的文件到一个文件(加上两个版本共享的文件)和非移动特定文件到另一个文件(以及共享文件)。此外,您可以稍后添加自动混淆和 gzip。然后,您可以将适当的源版本提供给适当的客户端。

    【讨论】:

    • 我正在缩小我的所有脚本,但代码量很大。未缩小,很难正确调试。此外,如果第 876 行有错误,我不知道该去哪里找 :(
    • @skyronic 在我自己的开发设置中,我有一个 no-minify 标志,我可以在调试时设置它,以便可以在 chrome 控制台和/或您最喜欢的编辑器中跟踪确切的行。然后构建脚本只会合并文件而不是混淆/缩小。
    • 我想尽可能避免连接所有文件。但现在我想别无选择:/ 我仍然在看是否值得重新构建以允许我独立加载所有文件
    • 在我看来,拥有多个文件并没有真正的好处,即使在调试时也是如此。现代调试器足够友好,所以我调试 4k 线项目没有问题。在应用适当的混淆和 gzip 压缩后,它也降至不到 100kb。
    【解决方案3】:

    如 cmets 中所述,所请求的功能是不可能的。 这不仅是一个技术问题,而且表明 应用程序的结构不恰当——应该改进设计。 现在,应该避免一种循环依赖。

    为了比较,您提到您将通过前向声明在 C++ 中解决它 的超类。但这也是不可能的。在 C++ 中, 为了声明一个子类,您需要 include 带有 超类的声明。并且存在循环依赖时无法解决问题。

    【讨论】:

    • 这不是循环依赖。如果我将所有内容都放在一个文件中,它就可以正常工作。不幸的是,JavaScript 会加载每个文件并逐个执行它们。通过连接脚本,对象在范围内。我只是想知道是否有一种方法可以在不连接脚本的情况下做到这一点。
    • 这是两个文件之间的循环依赖。 File_1 需要先加载 file_2。而file_2需要先加载file_1。
    【解决方案4】:

    无序js文件加载的简单逻辑:

    文件1:

    // ClassB: inherite from ClassA
    (function ClassB_Builder() {
      if(window.ClassB)return; // ClassB is already defined;
      if(!window.ClassA) { // ClassA is already not defined;
         setTimeout(ClassB_Builder,0); // shedule class building
         return;
      }
      ClassB=function() {
      }
      ClassB.prototype=new ClassA;
      ClassB.prototype.constructor=ClassB; // can be important for inheritance!!!
    })();
    

    文件2:

    // ClassA: base class
    (function ClassA_Builder() {
      ClassA=function() {
      }
    })();
    
    // ClassC: inherite from ClassB
    (function ClassC_Builder() {
      if(window.ClassC)return; // ClassC is already defined;
      if(!window.ClassB) { // ClassB is already not defined;
         setTimeout(ClassC_Builder,0); // shedule class building
         return;
      }
      ClassC=function() {
      }
      ClassC.prototype=new ClassB;
      ClassC.prototype.constructor=ClassC; // can be important for inheritance!!!
    })();
    

    【讨论】:

    • 哇,这是一个疯狂的 hack。我从未见过有人试图以这种方式解决它。有趣的是,它似乎非常适合开发,尽管我可能会有一个脚本在生产过程中删除这部分。
    猜你喜欢
    • 1970-01-01
    • 2012-03-14
    • 1970-01-01
    • 1970-01-01
    • 2016-06-19
    • 2011-05-08
    • 1970-01-01
    • 1970-01-01
    • 2010-10-22
    相关资源
    最近更新 更多