如何在 Vanilla JavaScript (JS) 中导入/导出类

Posted

技术标签:

【中文标题】如何在 Vanilla JavaScript (JS) 中导入/导出类【英文标题】:How to do import/export a class in Vanilla JavaScript (JS) 【发布时间】:2017-11-13 09:50:06 【问题描述】:

我正在使用纯 javascript 并尝试利用作为 ECMA-2015(ECMA v6) 版本的一部分出现的类和模块导入/导出的概念。

请看下面的代码sn-p:

rectangle.js 文件-

export default class  Rectangle
  constructor(height, width) 
    this.height = height;
    this.width = width;
  

myHifiRectangle.js文件-

import Rectangle from 'rectangle.js';

class MyHiFiRectangle extends Rectangle 
  constructor(height, width) 
      super(height,width);
      this.foo= "bar";  
 

我正在尝试在 html 网页 test.html 中使用上述 *.js 文件,如下所示:

<!DOCTYPE html>
<html lang = "en">
   <head>
      <meta charset = "UTF-8">
      <title>Javascipt by Rasik Bihari Tiwari</title>
       <script src="Scripts/rectangle.js"></script>
       <script src="Scripts/myHiFiRectangle.js"></script>
      <script type="text/javascript">
    
   var v = new MyHiFiRectangle(2,4);
   console.debug(v.foo);
      </script>
   </head>
   <body >

   </body>

</html>

现在我尝试在几个浏览器中浏览test.html文件,结果如下:

在谷歌浏览器上我得到以下错误:

Uncaught SyntaxError: Unexpected token export

在 Mozilla firefox 上出现以下错误:

SyntaxError: 出口声明只能出现在顶层 模块

SyntaxError:导入声明只能出现在顶层 一个模块

ReferenceError: MyHiFiRectangle 未定义[了解更多]

我尝试重新排序 HTML 文件的 head 标记中引用的 JS 文件,但没有任何区别。

P.S.我没有使用像 Babel 这样的转译器。我正在尝试查看 JS(ECMA-2015 版本)中对“类”和模块导出/导入构造的本机支持的工作原理以及它是如何工作的。

【问题讨论】:

我已经发布了您的问题的答案。 删除 &lt;script src="Scripts/rectangle.js"&gt;&lt;/script&gt; 并将 &lt;script src="Scripts/myHiFiRectangle.js"&gt;&lt;/script&gt; 替换为 &lt;script src="Scripts/myHiFiRectangle.js" type="module"&gt;&lt;/script&gt;。这解决了一个问题。另一个是 myHifiRectangle.js 不会创建全局变量(无论如何您都应该停止使用)。为此,请在myHifiRectangle.js 末尾添加window.MyHiFiRectangle = MyHiFiRectangle; 【参考方案1】:

为了完整起见,我在从@curiou.netter 的答案中得到提示后添加了一个答案。我在我的原始代码文件中按顺序指出了确切的错误。我能够在没有额外的script.js 文件的情况下实现它:

    在引用 JS 模块时,我们的脚本类型应该改为 module。我指的是myHiFiRectancle.js,就像使用srctag 作为src="Scripts/myHiFiRectangle.js" 的常规JS 文件一样。我还导入了MyHiFiRectangle 模块。以下是修复此错误后 head 标记现在在我的 test.html 文件中的外观:

    <head>
      <meta charset = "UTF-8">
      <title>Javascipt by Rasik Bihari Tiwari</title>
      <script type="module">
         import MyHiFiRectangle from './scripts/myHiFirectangle.js';
         var v = new MyHiFiRectangle(2,4);
         console.debug(v.foo);
      </script>
       </head>
    

    export defaultstatement 在myHiFiRectangle.js 文件中丢失。每个类都必须导出为模块才能变得有用。myHiFiRectangle.js 修复此错误后文件如下所示:

    import Rectangle from './rectangle.js';
    export default class MyHiFiRectangle extends Rectangle 
      constructor(height, width) 
      super(height,width);
      this.foo= "bar";  
     
    
    

    我的脚本文件在我引用模块的方式上有另一个错误。

    错误的方式:

    import Rectangle from 'rectangle.js';
    import MyHiFiRectangle from '/scripts/myHiFirectangle.js';
    

它会导致以下错误,这是不言自明的:

未捕获的类型错误:无法解析模块说明符“rectangle.js”。 相对引用必须以“/”、“./”或“../”开头。

正确方法:

    import Rectangle from './rectangle.js';
    import MyHiFiRectangle from './scripts/myHiFirectangle.js';

【讨论】:

【参考方案2】:

我经历了这个,我有一个将第三个 js 文件作为模块的解决方案。 rectangle.js 相同,myHifiRectangle.js 文件只有一处修改。

import Rectangle from './rectangle.js';

export default class MyHiFiRectangle extends Rectangle 
      constructor(height, width) 
      super(height,width);
      this.foo= "bar";  
   

现在,我们需要第三个文件,它是一个模块文件,比如说script.js

import MyHiFiRectangle from './myHifiRectangle.js'

var v = new MyHiFiRectangle(2,4);
console.log(v.foo);

现在,第三个文件script.js 应该成为一个模块。更多关于模块here。我在modelJS 文件夹下拥有所有三个文件。

<script type="module" src="/modelJS/script.js"></script>

现在,当您运行时,您应该会在开发人员工具的控制台选项卡中看到“栏”打印出来。

【讨论】:

太棒了!我不知道这个module 属性。可能是 Chrome 在撰写此问题时尚未实现它。顺便说一句,正如我自己的回答所提到的,我能够在我的 HTML 文件本身中使用 module 属性来实现它。我不需要单独的 script.js 文件来调用我的模块。我真的很感谢你为我的老问题付出了努力。我今天学到了一些新东西。【参考方案3】:

我想向您展示@Andy Gaskell 所发布内容的替代解决方案。

首先,您需要 babel 以确保您可以在浏览器中使用 ES6。这是为了确保您的代码仍然可以工作,因为某些浏览器(IE 等旧版浏览器)不支持现代 javascript(ES6 及更高版本)功能,例如导入/导出和类。

您可以添加以下脚本

`<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>`

在上述任何其他 javascript 文件之前。

其次,如果您将 javascript 类包含在内,这些类的范围将变为全局,即使它们驻留在自己的物理 js 文件中。

我在下面包含了工作示例,我对其进行了一些更改,以便它可以在代码 sn-p 中工作。您想将脚本替换为包含您的 javascript 文件的脚本,就像您在代码中所做的那样。

<!DOCTYPE html>
<html lang = "en">
   <head>
      <meta charset = "UTF-8">
      <title>Javascipt by Rasik Bihari Tiwari</title>
       <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>

      <!-- Replace them with script with src pointing to your javascript -->       
      <script type="text/javascript"> 
        class  Rectangle
          constructor(height, width) 
            this.height = height;
            this.width = width;
          
        

        class MyHiFiRectangle extends Rectangle 
          constructor(height, width) 
              super(height,width);
              this.foo= "bar";  
         
        
           
       var v = new MyHiFiRectangle(2,4);
       console.log(v.foo);
       </script>
   </head>
   <body >

   </body>

</html>

更新

好的。凉爽的!顺便说一句,如果我将所有类定义都带入脚本标签 我的 html 页面本身,那么我什至不需要引用 babel-core 在头部标签中。为什么需要它?

对于不支持 IE 等类的浏览器,您可能需要它。但是,如果对旧版浏览器的兼容性不是您的要求,那么您就不需要它。

...我什至需要进出口的东西吗?会是什么 当每个类时,本机 javascript 中模块导出的意义 或多或少是全球性的?

确实,您不需要导出-导入的东西,因为您的课程是全球性的。仅当您想使用模块系统时才使用它。如果您不使用导入/导出,您的类应该是全局的,因此应该可以工作。但万一它没有以某种方式。您可以通过将其附加到窗口对象来确保它全局存在,如下所示:

 window.myClass = class MyClass  /* Class definition */ 

【讨论】:

好的。凉爽的!顺便说一句,如果我将所有类定义带入我的 html 页面本身的脚本标记中,那么我什至不需要在 head 标记中引用 babel-core。为什么需要它? class 构造现在在从 ECMA-6 开始的 javascript 中得到原生支持。在使用原生 javascript 代码时,我应该需要转译器。转译器本身不发挥任何作用,但是当它们被一些模块捆绑器(如 webpack 或 browserify)调用时,它们就会发挥作用。 现在剩下的问题是,当我将我的类移动到单独的单独的 js 文件时,所有这些都不起作用。目前似乎正在爆发的事情是 exportimport 发生在单个 js 文件中。但是,如果我在使用它们的页面上以顺序/必需的方式(引用它们的顺序)包含了所有 js 文件(包含类文件),那么我什至需要导出导入的东西吗?当每个类都或多或少是全局的时,在原生 javascript 中导出模块有什么意义? 好的。因此,根据模块系统(假设我们正在谈论当前浏览器实现导出/导入的那一天) - 当我将导出语句用于驻留在自己的 js 文件中的类时,它突然变得非默认情况下是全局的,那些使用 ECMA6 规范中可用的 import 关键字显式导入它的脚本文件可用。这是一个公平的声明吗? 是的。我相信这是准确的。当您尝试导入不存在 export ... 的文件时(在您的 js 应用程序中),您可以看到这是真的。尝试导入所述模块的文件会抱怨。这是由于模块系统。代码的范围保留在文件中,除非它们被导入/导出。 为了完整起见,您能否在您的回答中添加一条注释,即导出/导入关键字截至目前仍无法使用,因为它们仍处于实施阶段(与 ECMA-6 规范有关)由各种浏览器供应商提供。只要我从中删除导出导入语句,我的代码 sn-p 也将起作用,因为所有类默认情况下都是全局的,即使它们驻留在自己的物理 *.js 文件中。【参考方案4】:

在most browsers 中,这是通过feature flag 启用的。

Chrome:转到 about:flags 并启用“实验性 Web 平台功能”。

Firefox 从版本 54 开始:dom.moduleScripts.enabled

Edge 15 或更新版本:在 about:flags 中启用“实验性 JavaScript 功能”。

【讨论】:

我可以看到访问 chrome 标志的 url 是 chrome://flags/ 我在启用您为 Chrome 和 Firefox 推荐的隐藏功能标志后尝试了它,但是当我浏览包含 class 构造的网页时,它们仍然在开发人员工具控制台选项卡中给出相同的错误输出在引用的 js 文件中。

以上是关于如何在 Vanilla JavaScript (JS) 中导入/导出类的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Vanilla JavaScript (JS) 中导入/导出类

如何使用 vanilla JavaScript 查找 div 的宽度?

如何从node js文件中调用一个vanilla JavaScript函数?

如何使用 Vanilla JavaScript/Ajax 获取地理位置 - 国家和城市

当弹出窗口关闭时,我将如何引发事件(jQuery 或 vanilla Javascript)?

如何仅使用 vanilla JavaScript 和 Node-API 将多个 div 元素包装在另一个元素节点周围?