scala.js 1.0 中的@JSGlobalScope(JavaScriptException、ReferenceError、var 未定义)
@JSGlobalScope in scala.js 1.0 (JavaScriptException, ReferenceError, var is not defined)
从 scala.js 0.6.x
迁移到 1.0
后,我有一些与 @JSGlobalScope
相关的代码损坏了。
我的用例是这样的:
- 有一个第 3 方库需要将某些全局变量设置为函数
- 加载并就绪后,它会调用此函数(按名称)
- 我在 scala.js
的全局范围内设置了这个函数
代码如下所示:
@js.native
@JSGlobalScope
object Globals extends js.Object {
var callbackFunctionFor3rdPartyLib: js.Function0[Unit] = js.native
}
然后我这样设置这个变量:
Globals.callbackFunctionFor3rdPartyLib = () => {
// do things
}
然后我将脚本添加到 DOM.
这是与 scala.js 0.6.x
一起工作的,但与 1.0
一起工作时出现如下异常:
scala.scalajs.js.JavaScriptException: ReferenceError: callbackFunctionFor3rdPartyLib is not defined
在 changelog for 1.0.0 中有一个 "Breaking changes" 部分提到了这一点:
Accessing a member that is not declared causes a ReferenceError to be thrown
...
js.Dynamic.global.globalVarThatDoesNotExist = 42
would previously create said global variable. In Scala.js 1.x, it also throws a ReferenceError.
我的问题是:
在 scala.js 1.0
中做这样的事情(创建一个新的全局变量)的正确方法是什么?
如果您知道自己将始终处于浏览器上下文中,则可以在 Globals
上使用 @JSGlobal("window")
而不是 @JSGlobalScope
,这等同于执行 window.myGlobalVarFor3rdPartyLib
在 JS 中。这样就可以了。
@js.native
@JSGlobal("window")
object Globals extends js.Object {
var callbackFunctionFor3rdPartyLib: js.Function0[Unit] = js.native
}
如果不是,但你使用的是 script(所以不是 CommonJS 也不是 ES 模块),最好的办法是使用
object Globals {
@JSExportTopLevel("myGlobalVarFor3rdPartyLib")
var foo: js.Function[Unit] = ...
}
请注意,Globals
现在是一个普通的 Scala 对象,而不是 JS 对象。
@JSExportTopLevel
在脚本的顶部创建了一个顶层 var myGlobalVarFor3rdPartyLib
,然后分配 Globals.foo
也会分配那个顶层 var
。
如果您不使用脚本,也不知道您将始终在浏览器中,那么您需要自己找出全局对象。 Scala.js 0.6.x 尝试为您这样做,但可能会失败,所以我们不再这样做了。您至少可以按照 the documentation of js.special.fileLevelThis
上的 "instructions" 来重现 Scala.js 0.6.x 正在做的事情。我在这里重复说明:
Using this value should be rare, and mostly limited to writing code
detecting what the global object is. For example, a typical detection
code--in case we do not need to worry of ES modules--looks like:
val globalObject = {
import js.Dynamic.{global => g}
if (js.typeOf(g.global) != "undefined" && (g.global.Object eq g.Object)) {
// Node.js environment detected
g.global
} else {
// In all other well-known environment, we can use the global `this`
js.special.fileLevelThis
}
}
Note that the above code is not comprehensive, as there can be JavaScript
environments where the global object cannot be fetched neither through
global
nor this
. If your code needs to run in such an environment, it
is up to you to use an appropriate detection procedure.
从 scala.js 0.6.x
迁移到 1.0
后,我有一些与 @JSGlobalScope
相关的代码损坏了。
我的用例是这样的:
- 有一个第 3 方库需要将某些全局变量设置为函数
- 加载并就绪后,它会调用此函数(按名称)
- 我在 scala.js 的全局范围内设置了这个函数
代码如下所示:
@js.native
@JSGlobalScope
object Globals extends js.Object {
var callbackFunctionFor3rdPartyLib: js.Function0[Unit] = js.native
}
然后我这样设置这个变量:
Globals.callbackFunctionFor3rdPartyLib = () => {
// do things
}
然后我将脚本添加到 DOM.
这是与 scala.js 0.6.x
一起工作的,但与 1.0
一起工作时出现如下异常:
scala.scalajs.js.JavaScriptException: ReferenceError: callbackFunctionFor3rdPartyLib is not defined
在 changelog for 1.0.0 中有一个 "Breaking changes" 部分提到了这一点:
Accessing a member that is not declared causes a ReferenceError to be thrown ...
js.Dynamic.global.globalVarThatDoesNotExist = 42
would previously create said global variable. In Scala.js 1.x, it also throws a ReferenceError.
我的问题是:
在 scala.js 1.0
中做这样的事情(创建一个新的全局变量)的正确方法是什么?
如果您知道自己将始终处于浏览器上下文中,则可以在 Globals
上使用 @JSGlobal("window")
而不是 @JSGlobalScope
,这等同于执行 window.myGlobalVarFor3rdPartyLib
在 JS 中。这样就可以了。
@js.native
@JSGlobal("window")
object Globals extends js.Object {
var callbackFunctionFor3rdPartyLib: js.Function0[Unit] = js.native
}
如果不是,但你使用的是 script(所以不是 CommonJS 也不是 ES 模块),最好的办法是使用
object Globals {
@JSExportTopLevel("myGlobalVarFor3rdPartyLib")
var foo: js.Function[Unit] = ...
}
请注意,Globals
现在是一个普通的 Scala 对象,而不是 JS 对象。
@JSExportTopLevel
在脚本的顶部创建了一个顶层 var myGlobalVarFor3rdPartyLib
,然后分配 Globals.foo
也会分配那个顶层 var
。
如果您不使用脚本,也不知道您将始终在浏览器中,那么您需要自己找出全局对象。 Scala.js 0.6.x 尝试为您这样做,但可能会失败,所以我们不再这样做了。您至少可以按照 the documentation of js.special.fileLevelThis
上的 "instructions" 来重现 Scala.js 0.6.x 正在做的事情。我在这里重复说明:
Using this value should be rare, and mostly limited to writing code detecting what the global object is. For example, a typical detection code--in case we do not need to worry of ES modules--looks like:
val globalObject = { import js.Dynamic.{global => g} if (js.typeOf(g.global) != "undefined" && (g.global.Object eq g.Object)) { // Node.js environment detected g.global } else { // In all other well-known environment, we can use the global `this` js.special.fileLevelThis } }
Note that the above code is not comprehensive, as there can be JavaScript environments where the global object cannot be fetched neither through
global
northis
. If your code needs to run in such an environment, it is up to you to use an appropriate detection procedure.