Javascript: почему определение переменной как результата функции не позволяет мне включить этот файл в HTML?
Скажем, у меня есть javascript в yum.js, который определяет объект, подобный этому
/* 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 - это "blueberry"". Это хорошо.
Однако, скажем, я хочу инициализировать pie_string до некоторого значения, возвращаемого функцией на другом объекте. Я меняю вкуснятину.js так создается pie_string с
pie_string: OtherObj.get_best_pie(),
//... in another file, OtherObj is defined with
get_best_pie: function() {
return "blueberry";
},
Когда я нажимаю кнопку, чтобы запустить тот же код, это не работает. вкуснятина.html говорит мне: "класс Yum имеет тип 'undefined'", и вызывает 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 и использоваться нормально.
Фу!
4 ответов:
Скажем, у меня есть javascript в yum.js, который определяет класс, подобный этому
У вас нет класса, в смысле шаблона, из которого можно создавать экземпляры, у вас есть объект. JavaScript не имеет классов в том смысле, как, скажем, Java, хотя вы можете использовать функции JS в качестве конструкторов объектов, если они вызываются с ключевым словом
new.1) Почему инициализация переменной в качестве возвращаемого значения функции не позволяет мне включить файл в HTML страницы?
Это не имеет никакого отношения к тому, включаете ли вы JS в качестве внешнего файла - даже если вы включили его в блок скрипта в вашем html-файле, он все равно не будет работать.
Проблема заключается в том, что вы не инициализируете переменные (множественное число), вы создаете единственную переменнуюYum, которая инициализируется из объектного литерала. То, что вы считаете "переменными", на самом деле является "свойствами" этого объекта. Синтаксис JS object literal не позволяет задайте свойства, которые ссылаются на другие свойства того же объекта - если вы думаете об этом, объект не существует до после, что строка кода выполняется и весь литерал объекта был оценен, и, конечно, вы не можете ссылаться на свойства чего-то, что еще не существует.То, что вы можете сделать, это инициализировать большинство свойств объекта через объектный литерал, а затем добавить свое
pie_stringсвойство:var Yum = { MY_ID: "[email protected]", MAX_PIES: 100, pies_eaten: [], get_best_pie: function() { return "blueberry"; }, eat_pie: function(pietype) { alert("mmmm, " + pietype); } } Yum.pie_string = Yum.get_best_pie();Или определить функцию
get_best_pie()независимо от литерала объекта, а затем установите СВОЙСТВОpie_stringиз него:function get_best_pie() { return "blueberry"; } var Yum = { pie_string : get_best_pie(), eat_pie: function(pietype) { alert("mmmm, " + pietype); }, // etc }2) есть ли способ получить сообщение об ошибке о неудачном включении, или молчаливый отказ является нормальным / ожидаемым от JavaScript?
Включение файла является успешным , но затем вы получаете ошибку JavaScript, которую вы уже видите (ту, которую вы процитировали).
Что касается вашего раздела "дополнительная информация", то код, который вы цитируете, опять же не создает переменная-с синтаксисом
:она должна быть извлечена из середины другого объектного литерала, создавая свойство. Если это так, то вполне допустимо, чтобы он был установлен на возврат из функции, определенной как свойство другого объекта, который уже существует - я думаю, вы найдете, что это происходит.
Потому что никогда не существует функции, вызываемой
get_best_pieдля вызоваЭто член объекта, в частности объекта, который еще не существует, поэтому даже если вы назовете его правильным именем (
Yum.get_best_pie()), он все равно не будет работать. вы должны определить функцию вне объекта, используяfunction get_best_pie(){ //... }Где-то в файле
во время создания объекта вы пытаетесь присвоить одно из свойств возвращаемому значению другого свойства (функции), объявленного в том же объекте!
В javascript объекты не имеют области видимости, только функции. Таким образом, ссылка на
get_best_pie()в определении объектаYumбудет относиться к окну, как вwindow.get_best_pie, которого не существует. ПосколькуYumеще не существует, функция, которую вы хотите вызвать (возвращаемое значение которой вы хотите сохранить вpie_string), еще не существует (до тех пор, пока объект не будет создан).Причина, по которой библиотеки, которые вы рассматриваете, работают, заключается в том, что они, по-видимому, не ссылаются на создаваемый объект контейнера. Они ссылаются на объект
Components, который уже должен быть создан (или, если нет, то фрагмент кода, который вы дали, существует внутри функции, которая не вызывается во время создания объекта).Javascript не имеет явного способа создания геттеров и сеттеров для свойств (до сих пор, хотя идеи по этому поводу находятся в работе). Это означает, что свойства должны быть простыми скалярами или объектами, которые разыменованы без скобок, или вместо этого вы должны использовать функции и разыменовать значение с помощью скобок, чтобы вызвать функцию (а не ссылаться на нее).
Вы можете рассмотреть что-то вроде следующего. Это близко к тому, что вы хотите, и позволяет вам продолжать использовать нотацию свойств, а не переключаться на функцию:
var Yum = { MY_ID: "[email protected]", MAX_PIES: 100, pies_eaten: [], get_best_pie: function() { return "blueberry"; }, eat_pie: function(pietype) { alert("mmmm, " + pietype); }, init: function() { Yum.pie_string = get_best_pie(); // other initialization here. } }; Yum.init();
Они ключ к тому, почему в том числе yum.ошибка js заключается в том, что объект, который мы вызываем для get_best_pie (), не является просто каким - либо объектом-это объект 'xpconnect wrapped', и поэтому вызов get_best_pie() требует дополнительных разрешений. Если бы OtherObj был просто обычным объектом, этот стиль инициализации работал бы, и мы могли бы включить HTML-файл. Но браузер не может получить доступ к OtherObj при попытке включить yum.JS. По-видимому, это оставляет нам только частичное определение объекта для Yum, вот почему он остается неопределенным и не может быть использован.
Причина, по которой это не удается для расширения Firefox, заключается в компонентах.classes-это объект в оболочке xpconnect, и у html-страницы нет разрешения на доступ к нему.
Включить вкуснятину.js в HTML-файле мы должны иметь соответствующие разрешения при инициализации
pie_string. Один из способов сделать это (как упоминалось @ErikE и @nnnnnn) - инициализировать переменную внутри функции - это позволяет вызывающему объекту запрашивать разрешение перед инициализацией, и вкуснятина.затем в JS могут быть проанализированы и включены обычно в yumtest.формат html.var Yum = { //... rest of object definition goes here init : function() { //important: assigning to 'pie_string' will create a new local variable; //use this.pie_string instead to reference the property of this class. this.pie_string = Components.classes["@mozilla.org/extension-string;1"] .getService(Components.interfaces.nsIPrefBranchInternal); } };Неправильные Решения
Вот два sqlutions, которыене работают. Кто-нибудь знает почему?
Попытка запросить разрешение внутри скрипта перед включением файла
<script type="text/javascript"> netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); </script> <script type="text/javascript" src="yum.js"></script>Попытка запросить разрешение, а затем включить файл динамически
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); var script = document.createElement("script"); script.type="text/javascript"; script.src="yum.js"; document.getElementsByTagName("head")[0].appendChild(script);
Comments