JS 模块如何防止内部定义的自定义元素暴露其 API?
How do JS modules prevent custom elements defined inside from exposing their APIs?
这不起作用:
<!-- wtf.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title></title>
<script type="module" src="./wtf.js"></script>
</head>
<body>
<script>
const myElement = document.createElement('my-element')
document.body.appendChild(myElement)
myElement.callMe()
</script>
</body>
</html>
// wtf.js
customElements.define('my-element', class extends HTMLElement {
constructor() {
super()
}
callMe() {
window.alert('I am called!')
}
})
Firefox 在 myElement.callMe()
行向我抛出一个讨厌的异常。显然,"myElement.callMe is not a function
".
我很困惑为什么会这样?据我了解,只要我输入 const myElement = document.createElement('my-element')
,我就会收到一个对象,其类型不是通用 HTMLElement
,而是我写的 class 的一个对象,它扩展了 HTMLElement
!而这个 class 暴露了 callMe
.
我已经确认我对模块的使用似乎是这里的罪魁祸首。此代码按预期工作:
<!-- wtf.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title></title>
</head>
<body>
<script>
customElements.define('my-element', class extends HTMLElement {
constructor() {
super()
}
callMe() {
window.alert('I am called!')
}
})
const myElement = document.createElement('my-element')
document.body.appendChild(myElement)
myElement.callMe()
</script>
</body>
</html>
是的,我知道模块中定义的内容仅限于此模块。但在这里,它甚至(对我而言)似乎都不是一个范围界定问题。例如,如果我 在模块中 做类似的事情:
function callMe() {/*blah blah */}
window.callMe = callMe
那么我将能够在模块 之外使用 callMe
无论如何,因为模块通过 export
以外的其他方式公开了此功能(这通过将其分配给全局 window
对象来节省时间)。
据我了解,我的用例中也应该发生同样的情况。即使我在模块范围内的 class 中定义了 callMe
,这个 class 方法应该可以在模块外部访问,因为它是一个对象的 属性通过调用 document.createElement('my-element')
公开的 class 的一部分。然而显然,这并没有发生。
这对我来说真的很奇怪。看起来好像模块是通过 与类型无关的函数纠缠来强制执行其作用域 return(!!) - 所以在这个在这种情况下,就好像模块神奇地强制 document.createElement
强制转换它在继承层次结构中 return 向上的对象(到 HTMLElement
)?!?!这让我兴奋不已。
有人可以解开我的困惑吗?
(如果我在模块内定义自定义元素,我如何将其 API 暴露在此模块外?)
问题是 <script type="module">
implicitly has a defer
attribute,所以它不会立即 运行。
Even though I define callMe in a class scoped to the module, this class method should be accessible outside of the module
是的,是的。问题只是它是异步定义的 :-) 要使用模块中的内容,您应该显式 import
该模块来声明依赖关系,以确保以正确的顺序对其进行评估。如果您的全局脚本不知何故 defer
红色,它也会起作用。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title></title>
<script type="module" src="./wtf.js"></script>
</head>
<body>
<script type="module">
import './wtf.js';
// ^^^^^^^^^^^^^^^^^^
const myElement = document.createElement('my-element')
document.body.appendChild(myElement)
myElement.callMe()
</script>
</body>
</html>
您还可以等待 window.onload 事件来执行您的内联脚本:
document.onload = () => {
const myElement = document.createElement('my-element')
document.body.appendChild(myElement)
myElement.callMe()
}
或者,您可以使用 classic <script>
加载来确保同步加载您的自定义元素:
<script src="./wtf.js"></script>
这不起作用:
<!-- wtf.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title></title>
<script type="module" src="./wtf.js"></script>
</head>
<body>
<script>
const myElement = document.createElement('my-element')
document.body.appendChild(myElement)
myElement.callMe()
</script>
</body>
</html>
// wtf.js
customElements.define('my-element', class extends HTMLElement {
constructor() {
super()
}
callMe() {
window.alert('I am called!')
}
})
Firefox 在 myElement.callMe()
行向我抛出一个讨厌的异常。显然,"myElement.callMe is not a function
".
我很困惑为什么会这样?据我了解,只要我输入 const myElement = document.createElement('my-element')
,我就会收到一个对象,其类型不是通用 HTMLElement
,而是我写的 class 的一个对象,它扩展了 HTMLElement
!而这个 class 暴露了 callMe
.
我已经确认我对模块的使用似乎是这里的罪魁祸首。此代码按预期工作:
<!-- wtf.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title></title>
</head>
<body>
<script>
customElements.define('my-element', class extends HTMLElement {
constructor() {
super()
}
callMe() {
window.alert('I am called!')
}
})
const myElement = document.createElement('my-element')
document.body.appendChild(myElement)
myElement.callMe()
</script>
</body>
</html>
是的,我知道模块中定义的内容仅限于此模块。但在这里,它甚至(对我而言)似乎都不是一个范围界定问题。例如,如果我 在模块中 做类似的事情:
function callMe() {/*blah blah */}
window.callMe = callMe
那么我将能够在模块 之外使用 callMe
无论如何,因为模块通过 export
以外的其他方式公开了此功能(这通过将其分配给全局 window
对象来节省时间)。
据我了解,我的用例中也应该发生同样的情况。即使我在模块范围内的 class 中定义了 callMe
,这个 class 方法应该可以在模块外部访问,因为它是一个对象的 属性通过调用 document.createElement('my-element')
公开的 class 的一部分。然而显然,这并没有发生。
这对我来说真的很奇怪。看起来好像模块是通过 与类型无关的函数纠缠来强制执行其作用域 return(!!) - 所以在这个在这种情况下,就好像模块神奇地强制 document.createElement
强制转换它在继承层次结构中 return 向上的对象(到 HTMLElement
)?!?!这让我兴奋不已。
有人可以解开我的困惑吗?
(如果我在模块内定义自定义元素,我如何将其 API 暴露在此模块外?)
问题是 <script type="module">
implicitly has a defer
attribute,所以它不会立即 运行。
Even though I define callMe in a class scoped to the module, this class method should be accessible outside of the module
是的,是的。问题只是它是异步定义的 :-) 要使用模块中的内容,您应该显式 import
该模块来声明依赖关系,以确保以正确的顺序对其进行评估。如果您的全局脚本不知何故 defer
红色,它也会起作用。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title></title>
<script type="module" src="./wtf.js"></script>
</head>
<body>
<script type="module">
import './wtf.js';
// ^^^^^^^^^^^^^^^^^^
const myElement = document.createElement('my-element')
document.body.appendChild(myElement)
myElement.callMe()
</script>
</body>
</html>
您还可以等待 window.onload 事件来执行您的内联脚本:
document.onload = () => {
const myElement = document.createElement('my-element')
document.body.appendChild(myElement)
myElement.callMe()
}
或者,您可以使用 classic <script>
加载来确保同步加载您的自定义元素:
<script src="./wtf.js"></script>