Javascript:为什么将变量定义为函数的结果会阻止我将该文件包含在 HTML 中?
假设我在 yum.js 中有一些 javascript 定义了这样的对象,
/* yum.js */
var Yum = {
MY_ID: "[email protected]",
MAX_PIES: 100,
pies_eaten: [],
pie_string: "blueberry",
eat_pie: function(pietype) {
alert("mmmm, " + pietype);
}
}
我有一个 HTML 文件 yumtest.html,我想在其中包含 yum.js 并调用一些函数。 HTML 看起来像
<!-- yumtest.html -->
<html>
<head>
<script type="text/javascript" src="yum.js"></script>
<script type="text/javascript">
function run_tests() {
alert("The Yum class is type '" + typeof Yum + "'.");
alert("pie_string is '" + Yum.pie_string + "'.");
Yum.eat_pie("apple");
}
</script>
</head>
<body>
<input type="button" value="Click to run tests" onclick="run_tests()">
</body>
</html>
这样工作正常:我可以单击按钮,警报告诉我“pie_string 是‘蓝莓’”。这很好。
但是,假设我想将 pie_string 初始化为另一个对象上的函数返回的某个值。我更改了 yum.js,以便使用创建的ie_string
pie_string: OtherObj.get_best_pie(),
//... in another file, OtherObj is defined with
get_best_pie: function() {
return "blueberry";
},
当我单击按钮运行相同的代码时,这不起作用。 yumtest.html 告诉我“Yum 类的类型为‘未定义’”,并且对 Yum.x 属性或方法的调用不起作用。代码执行停止。
所以我想知道:
1) 为什么将变量初始化为函数的返回值会阻止我将该文件包含在 HTML 页面中? (我目前的猜测是,当文件被解析并添加到 HTML 时,该函数无法执行和返回 - 请参阅下面的额外信息)
2) 有没有办法获取有关的错误消息包含失败,或者静默失败是 JavaScript 正常/预期的吗?
额外信息
我之所以问这个问题是因为我在 Firefox 扩展中看到了这种情况。多个变量以这种方式初始化,用于
varname : Components.classes["@mozilla.org/extension-name-string;1"]
.getService(Components.interfaces.nsIPrefBranchInternal),
加载各种用户首选项。这工作正常并且扩展运行正常,因此推测这是有效的 Javascript 代码。或者至少 - 在某些情况下有效?也许作为实际 Javascript 运行与被解析并包含在 HTML 中之间的差异会阻止函数运行并返回值?
解决方法
我想一种解决方法是将 Yum 重新定义为一个类,并在构造函数中初始化变量。
对于那些不能或不想将代码重构为类的人来说,这是个好消息。您可以将变量定义就地保留,但仍然通过将变量定义为函数并计算内部返回值来将该文件包含在 HTML 中。例如,我们可以这样做
pie_string: function() {
// do some calculations here;
// whatever we would have done in get_best_pie()
return "blueberry";
},
这意味着您必须将 Yum.pie_string
的所有调用更改为 Yum.pie_string()
(或者您可能想重命名该函数 ' get_pie_string()
'所以很清楚......)。但是 javascript 文件可以包含在 HTML 中并正常使用。
唷!
Say I have some javascript in yum.js that defines an object like this
/* yum.js */
var Yum = {
MY_ID: "[email protected]",
MAX_PIES: 100,
pies_eaten: [],
pie_string: "blueberry",
eat_pie: function(pietype) {
alert("mmmm, " + pietype);
}
}
I have an HTML file yumtest.html where I want to include yum.js and call some functions. The HTML looks like
<!-- yumtest.html -->
<html>
<head>
<script type="text/javascript" src="yum.js"></script>
<script type="text/javascript">
function run_tests() {
alert("The Yum class is type '" + typeof Yum + "'.");
alert("pie_string is '" + Yum.pie_string + "'.");
Yum.eat_pie("apple");
}
</script>
</head>
<body>
<input type="button" value="Click to run tests" onclick="run_tests()">
</body>
</html>
This works fine: I can click the button and the alert tells me "pie_string is 'blueberry'". This is good.
However, say I want to initialize pie_string to some value returned by a function on another object. I change yum.js so pie_string is created with
pie_string: OtherObj.get_best_pie(),
//... in another file, OtherObj is defined with
get_best_pie: function() {
return "blueberry";
},
When I click the button to run the same code, this doesn't work. yumtest.html tells me "The Yum class is type 'undefined'", and calls to Yum.x properties or methods don't work. Code execution stops.
So I'm wondering:
1) Why does initializing the variable as a function's return value prevent me from including the file in an HTML page?
(my current guess is that the function is unable to execute and return when the file is being parsed and added to the HTML - see Extra Info below)
2) Is there a way to get an error message about the inclusion failing, or is silent failure normal/expected from JavaScript?
Extra Information
The reason I ask is because I see this happening in a Firefox extension. Several variables are being initialized this way using
varname : Components.classes["@mozilla.org/extension-name-string;1"]
.getService(Components.interfaces.nsIPrefBranchInternal),
to load various user preferences. This works fine and the extension runs normally, so presumably this is valid Javascript code. Or at least - valid in some situations? Perhaps the difference between running as actual Javascript and being parsed and included in HTML prevents the function from running and returning a value?
Workaround
I suppose one workaround would be to redefine Yum as a class, and initialize variables in the constructor.
For those who can't or don't want to refactor their code into a class, good news. You can keep the variable defined in-place but still include that file in HTML by defining the variable as a function and calculating the return value inside. For example, we could do
pie_string: function() {
// do some calculations here;
// whatever we would have done in get_best_pie()
return "blueberry";
},
This means you have to change all of you calls of Yum.pie_string
to Yum.pie_string()
(or perhaps you want to rename the function 'get_pie_string()
' so it's clear...). But the javascript file can then be included inside HTML and used normally.
phew!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
,从可以创建实例的模板的意义上来说,您没有一个类,您有一个对象。 JavaScript 不像 Java 那样具有类,尽管如果使用
new
关键字调用,您可以使用 JS 函数作为对象构造函数。这与您是否将 JS 作为外部文件包含在内无关 - 即使您将其包含在 html 文件的脚本块中,它仍然无法工作。
问题是您没有初始化变量(复数),您正在创建一个从对象文字初始化的单个变量
Yum
。您所认为的“变量”实际上是该对象的“属性”。 JS 对象字面量语法不允许您设置引用同一对象的其他属性的属性 - 如果您考虑一下,直到该行代码运行和整个对象字面量之后,该对象才存在已经被评估了,当然你不能引用尚不存在的东西的属性。您可以做的是通过对象文字初始化大多数对象属性,然后添加您的
pie_string
属性:或者定义
get_best_pie()
函数独立于对象字面量,然后从中设置pie_string
属性:文件的包含成功,但是随后您收到了一个您已经看到的 JavaScript 错误(您引用的错误)。
关于您的“额外信息”部分,您引用的代码再次不是创建变量 - 使用
:
语法,它必须是从另一个对象文字中间提取的内容,创建一个属性。如果是这样,那么将其设置为定义为已存在的不同对象的属性的函数的返回是完全有效的 - 我想您会发现这就是发生的事情。You don't have a class, in the sense of a template from which instances can be created, you have an object. JavaScript doesn't have classes in the sense that, say, Java does, though you can use JS functions as object constructors if called with the
new
keyword.This has nothing to do with whether you are including the JS as an external file - even if you included it in a script block in your html file it still wouldn't work.
The problem is that you are not initialising variables (plural), you are creating a single variable
Yum
that is being initialised from an object literal. What you are thinking of as "variables" are really "properties" of that object. JS object literal syntax does not allow you to set properties that reference other properties of the same object - if you think about it the object doesn't exist until after that line of code runs and the entire object literal has been evaluated, and of course you can't refer to properties of something that doesn't exist yet.What you can do is initialise most of the object properties via the object literal and then add your
pie_string
property afterwards:Or define the
get_best_pie()
function independent of the object literal and then set thepie_string
property from it:The inclusion of the file is succeeding, but then you are getting a JavaScript error which you are already seeing (the one you quoted).
Regarding your "extra information" section, the code you quote is, again, not creating a variable - with the
:
syntax it must be an extract from the middle of another object literal, creating a property. If so, it is perfectly valid for it to be set to the return from a function defined as a property of a different object that already exists - I think you'll find that's what's happening.因为从来没有一个名为
get_best_pie
的函数供您调用它是一个对象的成员,特别是一个尚不存在的对象,所以即使您使用正确的名称调用它(
Yum.get_best_pie()
)它仍然无法工作。 使用对象外部定义函数您需要在文件中的某处
because there is never a function called
get_best_pie
for you to callIt is a member of an object, notably an object that doesn't exist yet, so even if you called it by the correct name (
Yum.get_best_pie()
) it still wouldn't work. you need to define the function outside the object usingin the file somewhere
在对象创建时,您试图将其中一个属性分配给同一对象中声明的另一个属性(函数)的返回值!
在 JavaScript 中,对象没有作用域,只有函数。因此,
Yum
对象定义中对get_best_pie()
的引用将作用于窗口,如window.get_best_pie
中那样,而该窗口并不存在。因为Yum
尚不存在,所以您要调用的函数(您要存储在pie_string
中的返回值)尚不存在(直到创建对象之后) 。您正在查看的库起作用的原因是因为它们显然没有引用正在创建的容器对象。它们引用必须已实例化的
Components
对象(或者,如果没有实例化,则您提供的代码片段存在于在对象创建时未调用的函数内)。Javascript 没有明确的方法来创建属性的 getter 和 setter(到目前为止,尽管这方面的想法正在酝酿中)。这意味着属性必须是不带括号取消引用的简单标量或对象,或者,您必须改用函数并使用括号取消引用值以调用函数(而不是引用它)。
您可能会考虑类似以下内容。它接近您想要的,并允许您继续使用属性表示法而不是切换到函数:
At object creation time you are trying to assign one of the properties to the return value of another property (a function) declared in the same object!
In javascript, objects have no scope, only functions. Thus, the reference to
get_best_pie()
in theYum
object's definition will scope to the window as inwindow.get_best_pie
, which does not exist. BecauseYum
does not exist yet, the function you want to call (whose return value you want to store inpie_string
) does not exist yet (until after the object is created).The reason the libraries you're looking at work is because they apparently are not referring to the container object that is being created. They reference the
Components
object which must already have been instantiated (or, if not, then the code snippet you gave exists inside a function which is not invoked at object creation time).Javascript has no explicit way to create getters and setters for properties (so far, though ideas on this are in the works). This means that properties must be simple scalars or objects that are dereferenced without parentheses, or, you must use functions instead and dereference the value using parentheses to invoke the function (rather than refer to it).
You might consider something like the following. It is close to what you want and lets you continue to use property notation rather than switching to a function:
包含 yum.js 失败的关键在于,我们为 get_best_pie() 调用的对象不仅仅是任何对象 - 它是一个“xpconnect 包装”对象,因此调用 get_best_pie() 需要额外的权限。如果 OtherObj 只是一个常规对象,那么初始化样式就可以工作,并且我们可以包含 HTML 文件。但在尝试包含 yum.js 时,浏览器无法访问 OtherObj。据推测,这给我们留下了 Yum 的部分对象定义,这就是它保持未定义且无法使用的原因。
Firefox 扩展失败的原因是 Components.classes 是一个“xpconnect 包装”对象,并且 html 页面没有访问它的权限。
要将 yum.js 包含在 HTML 文件中,我们需要在初始化
pie_string
时拥有适当的权限。一种方法(如 @ErikE 和 @nnnnnn 提到的)是在函数内初始化变量 - 这允许调用者在初始化之前请求许可,然后 yum.js 可以被解析并正常包含在 yumtest.html 中。不正确的解决方案
这里有两个不起作用的解决方案。有人知道为什么吗?
在包含文件之前尝试在脚本内请求许可
尝试请求权限然后动态包含文件
They key to why including yum.js fails is that the object we're calling for get_best_pie() is not just any object - it's an 'xpconnect wrapped' object, and so calling get_best_pie() requires extra permissions. If OtherObj was just a regular object that style of initialization would work, and we could include the HTML file. But the browser fails to access OtherObj when trying to include yum.js. Presumably this leaves us with only a partial object definition for Yum, which is why it remains undefined and can't be used.
The reason this fails for the Firefox extension is because Components.classes is an 'xpconnect wrapped' object, and the html page doesn't have permission to access it.
To include yum.js in an HTML file we need to have the proper permissions when initializing
pie_string
. One way to do that (as mentioned by @ErikE and @nnnnnn) is to initialize the variable inside a function - this allows the caller to ask for permission before initializing, and yum.js can then be parsed and included normally in yumtest.html.Improper Solutions
Here are two sqlutions that don't work. Does anybody know why?
Trying to ask for permission inside a script before including the file
Trying to ask for permission and then including the file dynamically