从 JavaScript 调用 @JSFunction,TypeError:找不到对象的默认值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14561874/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 21:59:05  来源:igfitidea点击:

Calling @JSFunction from JavaScript, TypeError: Cannot find default value for object

javajavascriptrhino

提问by oschrenk

I'm calling a @JSFunctionannotated method of a ScriptableObject

我正在调用一个带@JSFunction注释的方法ScriptableObject

The JavaScript file

JavaScript 文件

Target = Packages.com.acme.rhino.Target;

function evaluate() {
    var t = Target();
    t.addModifier("foobar", 1);
    return t;
}

The Java File

Java文件

public class Target extends ScriptableObject {
    private static final long serialVersionUID = 1L;
    public List<Modifier> modifiers = new LinkedList<>();

    @JSConstructor
    public Target() {
    }

    @JSFunction
    public void addModifier(final String message, final int value) {
        modifiers.add(new Modifier(message, value));
    }

    public int getValue() {
        int sum = 0;
        for (final Modifier modifier : modifiers) {
            sum += modifier.getValue();
        }
        return sum;
    }

    @Override
    public String getClassName() {
        return "Target";
    }
}

But I get

但我得到

org.mozilla.javascript.EcmaError: TypeError: Cannot find default value for object.
    at org.mozilla.javascript.ScriptRuntime.constructError(ScriptRuntime.java:3687)
    at org.mozilla.javascript.ScriptRuntime.constructError(ScriptRuntime.java:3665)
    at org.mozilla.javascript.ScriptRuntime.typeError(ScriptRuntime.java:3693)
    at org.mozilla.javascript.ScriptRuntime.typeError1(ScriptRuntime.java:3705)
    at org.mozilla.javascript.ScriptableObject.getDefaultValue(ScriptableObject.java:976    )
    at org.mozilla.javascript.ScriptableObject.getDefaultValue(ScriptableObject.java:895    )
    at org.mozilla.javascript.ScriptRuntime.toString(ScriptRuntime.java:761)
    at org.mozilla.javascript.ScriptRuntime.notFunctionError(ScriptRuntime.java:3774)
    at org.mozilla.javascript.ScriptRuntime.getPropFunctionAndThisHelper(ScriptRuntime.    java:2269)
    at org.mozilla.javascript.ScriptRuntime.getPropFunctionAndThis(ScriptRuntime.    java:2251)
    at org.mozilla.javascript.optimizer.OptRuntime.callProp0(OptRuntime.java:83)
    at org.mozilla.javascript.gen.script_5._c_evaluate_1(script:6)
    at org.mozilla.javascript.gen.script_5.call(script)
    at org.mozilla.javascript.ContextFactory.doTopCall(ContextFactory.java:394)
    at org.mozilla.javascript.ScriptRuntime.doTopCall(ScriptRuntime.java:3091)
    at org.mozilla.javascript.gen.script_5.call(script)

and don't know where to go from there. When I don't call addModifiermethod the given code works, and given the error notFunctionErrordown in the stack trace I think that Rhino doesn't interpret the given method as a JavaScript Function.

不知道从那里去哪里。当我不调用addModifier方法时,给定的代码有效,并且notFunctionError在堆栈跟踪中给出了错误,我认为 Rhino 不会将给定的方法解释为 JavaScript 函数。

  • OSX 10.8.2
  • Java 7
  • Rhino 1.7R4
  • OSX 10.8.2
  • 爪哇 7
  • 犀牛1.7R4

Complete Maven project that reproduces the error can be found here

可以在此处找到重现错误的完整 Maven 项目

采纳答案by Tim Schaub

tl;dr see thesetwoalternatives.

tl;dr 看到两种选择。

The problem with the approach above is that Target.prototypeis not properly set up in the script scope. See the static ScriptableObject.defineClass()method for details on how to properly define prototypes in a script scope.

上述方法的问题Target.prototype在于未在脚本范围内正确设置。有关ScriptableObject.defineClass()如何在脚本范围内正确定义原型的详细信息,请参阅静态方法。

You have a couple alternatives for providing the Targetconstructor to your scripts. The first alternativewould be to always define the Targetconstructor for all scripts. This works well if you know ahead of time that you want Targetto be globally available. This basically comes down to the following:

您有几种替代方法可以为Target脚本提供构造函数。第一种选择是始终Target为所有脚本定义构造函数。如果您提前知道您希望Target在全球范围内可用,那么这很有效。这基本上归结为以下几点:

final Context context = Context.enter();
try {
  final ScriptableObject scope = context.initStandardObjects();
  ScriptableObject.defineClass(scope, Target.class, false, true);
  context.evaluateString(scope, script, "script", 1, null);
  // etc.
} finally {
  Context.exit();
}

If instead you want the script author to decide which constructors are necessary, the second alternativeis to provide the defineClassfunction to scripts. With this function, script authors can "import" any scriptable objects on their class path (which may be more than you want to allow). To provide the defineClassfunctions to scripts, do the following after entering the context:

相反,如果您希望脚本作者决定哪些构造函数是必需的,则第二种选择defineClass向脚本提供该函数。使用此功能,脚本作者可以在其类路径上“导入”任何可编写脚本的对象(这可能超出您的预期)。要为defineClass脚本提供功能,请在进入上下文后执行以下操作:

final Context context = Context.enter();
try {
  final ScriptableObject scope = context.initStandardObjects();
  scope.defineFunctionProperties(
          new String[] {"defineClass"},
          Global.class,
          ScriptableObject.DONTENUM);

  context.evaluateString(scope, script, "script", 1, null);
  // etc.
} finally {
  Context.exit();
}

And then, the JavaScript author makes use of the Targetconstructor with the following:

然后,JavaScript 作者使用Target具有以下内容的构造函数:

defineClass("com.acme.rhino.Target");
// whatever `getClassName()` returns is now available
var target = new Target();

In both of the above branches, I've made a couple other changes that set you up better if you add more to the Targetconstructor. The zero argument constructor doesn't need the @JSConstructorannotation. If you later want to have a constructor that accepts arguments, this zero argument constructor will be used as the prototype constructor, and you can use the @JSConstructorannotation on a method that will be used to initialize your object. Depending on how you author this constructor method, it will become important to use the newkeyword in your JavaScript.

在上述两个分支中,我做了一些其他更改,如果您向Target构造函数添加更多内容,这些更改会更好。零参数构造函数不需要@JSConstructor注释。如果您以后想要一个接受参数的构造函数,这个零参数构造函数将用作原型构造函数,您可以@JSConstructor在将用于初始化对象的方法上使用注释。根据您编写此构造函数方法的方式,new在 JavaScript 中使用关键字变得很重要。

In short, the Packages.com.acme...syntax is notuseful for getting access to ScriptableObjectconstructors from scripts.

简而言之,Packages.com.acme...语法对于从脚本访问构造函数没有ScriptableObject

回答by elxala

I get it working (very similar code) by using the new operator. In your example doing

我通过使用 new 运算符让它工作(非常相似的代码)。在你的例子中

 function evaluate() {
      var t = new Target();
      ...

should work as well.

也应该工作。