【问题标题】:ES6 Modules: Undefined onclick function after importES6 模块:导入后未定义的 onclick 函数
【发布时间】:2017-11-19 07:10:12
【问题描述】:

我正在测试 ES6 模块,并希望让用户使用 onclick 访问一些导入的函数:

test.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Module Test</title>
</head>
<body>
    <input type="button" value="click me" onclick="hello();"/>
    <script type="module">import {hello} from "./test.js";</script>
</body>
</html>

test.js:

export function hello() {console.log("hello");}

当我点击按钮时,开发者控制台显示:ReferenceError: hello is not defined。如何从模块中导入函数,以便它们可用作 onclick 函数?

我正在使用 Firefox 54.0,并将 dom.moduleScripts.enabled 设置为 true

【问题讨论】:

  • 页面加载完毕后能不能打开控制台手动执行hello?
  • 这个功能被标记为实验性的,我现在不会使用它。你最好编译(Babel?)你的代码,以确保它可以在任何浏览器中工作,不是吗?

标签: javascript ecmascript-6 es6-modules


【解决方案1】:

模块创建一个范围以避免名称冲突。

要么将你的函数暴露给window对象

import {hello} from './test.js'

window.hello = hello

或使用addEventListener 绑定处理程序。 Demo

<button type="button" id="hello">Click Me</button>
<script type="module">
  import {hello} from './test.js'

  document.querySelector('#hello').addEventListener('click', hello)
</script>

【讨论】:

  • 绝对选择选项二。在最好的情况下,处理全局变量是一件痛苦的事情。
【解决方案2】:

ES6 模块中的模块范围:

当您使用type="module" 以下列方式导入脚本时:

<script type="module">import {hello} from "./test.js";</script>

您正在创建一个称为模块范围的特定范围。这是模块范围相对于其他级别范围的地方。从全局开始,它们是:

  1. 全局范围:在最外层的模块之外的所有声明(即不在函数中,对于 constlet 不在块中)可从 Javascript 运行时环境中的任何位置访问
  2. 模块范围:模块内的所有声明都以模块为范围。当其他 javascipt 尝试访问这些声明时,它会抛出一个引用错误。
  3. 函数作用域:在函数体内(顶层)声明的所有变量都是函数作用域
  4. 块作用域:letconst 变量是块作用域

您收到了referenceError,因为hello() 函数是在模块中声明的,该模块是模块作用域。正如我们之前看到的,模块范围内的声明仅在该模块内可用,并且您尝试在模块外使用它。

当我们显式将它放在 window 对象上时,我们可以在模块内进行全局声明,以便我们可以在模块外使用它。例如:

window.hello = hello;  // putting the hello function as a property on the window object

【讨论】:

  • 哦,这是否意味着没有模块类型的脚本标签中的函数被放在全局范围内,并且可以在外部脚本中使用?
【解决方案3】:

虽然接受的答案是正确的,但一旦您开始从多个模块导入或声明多个函数,它的扩展性就会很差。另外,正如@Quentin 所指出的,它存在全球命名空间污染的风险。

我更喜欢稍微修改

import { doThis, doThat } from './doStuff.js'
import { foo1, foo2 } from './foo.js'

function yetAnotherFunction() { ... }

window._allTheFns = { doThis, doThat, foo1, foo2, yetAnotherFunction }

// or, if you prefer, can subdivide

window._doStuffjs = { doThis, doThat }
window._foojs = { foo1, foo2 }
  1. 使用“不寻常的”属性名称来(希望)避免全局命名空间问题
  2. 无需立即附加(或忘记附加)EventListener。
  3. 您甚至可以将监听器放在 HTML 中,例如&lt;button onclick="window._doStuffjs.foo1(this)"&gt;Click Me&lt;/button&gt;

【讨论】:

    猜你喜欢
    • 2018-03-18
    • 2019-01-10
    • 1970-01-01
    • 2015-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-28
    相关资源
    最近更新 更多