Javascript: Почему определение переменной в результате функции не позволяет мне включить этот файл в HTML? - PullRequest
2 голосов
/ 05 января 2012

Скажем, у меня есть некоторый javascript в yum.js, который определяет объект, подобный этому

/* yum.js */

var Yum = {
    MY_ID: "yum@yum.yum",

    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 is 'blueberry'". Это хорошо.

Однако, скажем, я хочу инициализировать pie_string некоторым значением, возвращаемым функцией другого объекта. Я изменяю yum.js, поэтому pie_string создается с

pie_string: OtherObj.get_best_pie(),

//... in another file, OtherObj is defined with
get_best_pie: function() {
    return "blueberry";
},

Когда я нажимаю кнопку, чтобы запустить тот же код, это не работает. yumtest.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 ]

4 голосов
/ 05 января 2012

Скажем, у меня есть некоторый javascript в yum.js, который определяет такой класс

У вас нет класса в смысле шаблона, из которого можно создавать экземплярыУ вас есть объект.JavaScript не имеет классов в том смысле, как, скажем, Java, хотя вы можете использовать функции JS в качестве конструкторов объектов при вызове с ключевым словом new.

1) Почему происходит инициализация переменнойкак возвращаемое значение функции не позволяет мне включить файл в страницу HTML?

Это не имеет никакого отношения к тому, включаете ли вы JS в качестве внешнего файла - даже если вы включили его в скриптБлок в вашем HTML-файле все равно не будет работать.

Проблема в том, что вы не инициализируете переменные (множественное число), вы создаете одну переменную Yum, которая инициализируетсяиз буквального объекта.То, что вы считаете «переменными», на самом деле является «свойствами» этого объекта.Синтаксис литерала объекта JS не позволяет вам устанавливать свойства, которые ссылаются на другие свойства того же объекта - если вы думаете об этом, объект не существует до после , когда выполняется строка кода и весь литерал объекта имеетбыла оценена, и, конечно, вы не можете ссылаться на свойства чего-то, что еще не существует.

Что вы можете сделать, это инициализировать большинство свойств объекта через литерал объекта изатем добавьте свойство pie_string:

var Yum = {
   MY_ID: "yum@yum.yum",
   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, которую вы уже видите (ту, которую вы цитировали).

Относительно вашего раздела «дополнительная информация», кодВы цитируете, опять же, не создавая переменную - с синтаксисом : она должна быть извлечением из середины другого литерала объекта, создавая свойство.Если это так, то вполне допустимо, чтобы для него было установлено возвращение из функции, определенной как свойство другого объекта, который уже существует - я думаю, вы обнаружите, что именно так и происходит.

3 голосов
/ 05 января 2012

потому что никогда не существует функции с именем get_best_pie для вызова

Это член объекта, в частности объект, который еще не существует, поэтому даже если вы вызвали его по правильномуимя (Yum.get_best_pie()) это все равно не будет работать.вам нужно определить функцию вне объекта, используя

function get_best_pie(){
    //...
}

в файле где-то

1 голос
/ 05 января 2012

Во время создания объекта вы пытаетесь присвоить одно из свойств возвращаемому значению другого свойства (функции), объявленного в том же объекте!

В javascript объекты не имеют области видимости, только функции.Таким образом, ссылка на get_best_pie() в определении объекта Yum будет охватывать окно, как в window.get_best_pie, которого не существует.Поскольку Yum еще не существует, функция, которую вы хотите вызвать (чье возвращаемое значение вы хотите сохранить в pie_string), еще не существует (до тех пор, пока объект не будет создан).

Причинабиблиотеки, с которыми вы работаете, потому что они, очевидно, не ссылаются на создаваемый контейнерный объект.Они ссылаются на объект Components, который уже должен быть создан (или, если нет, то предоставленный вами фрагмент кода существует внутри функции, которая не вызывается во время создания объекта).

Javascript не имеет явного способасоздавать геттеры и сеттеры для свойств (пока что идеи по этому поводу находятся в разработке).Это означает, что свойства должны быть простыми скалярами или объектами, которые разыменовываются без скобок, или вы должны вместо этого использовать функции и разыменовать значение с помощью скобок для вызова функции (а не ссылки на нее).что-то вроде следующего.Он близок к тому, что вы хотите, и позволяет вам продолжать использовать нотацию свойств, а не переключаться на функцию:

var Yum = {
    MY_ID: "yum@yum.yum",
    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();
0 голосов
/ 05 января 2012

Они объясняют, почему включение yum.js дает сбой, заключается в том, что объект, который мы вызываем для get_best_pie (), - это не просто какой-либо объект - это объект 'xpconnect wrapped', поэтому для вызова get_best_pie () требуются дополнительные разрешения.Если бы OtherObj был просто обычным объектом, этот стиль инициализации работал бы, и мы могли бы включить файл HTML.Но браузер не может получить доступ к OtherObj при попытке включить yum.js.Предположительно, это оставляет нам только частичное определение объекта для Yum, поэтому оно остается неопределенным и не может быть использовано.

Причина, по которой это не удается для расширения Firefox, заключается в том, что Components.classes является упаковкой xpconnect'объекта, и html-страница не имеет прав доступа к нему.

Чтобы включить yum.js в файл HTML, у нас должны быть соответствующие разрешения при инициализации pie_string.Один из способов сделать это (как упомянуто @ErikE и @nnnnnn) - инициализировать переменную внутри функции - это позволяет вызывающей стороне запрашивать разрешение перед инициализацией, а затем yum.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);
}

};

Неправильные решения

Вот два решения, которые не работают.Кто-нибудь знает почему?

  1. Попытка запросить разрешение внутри скрипта перед включением файла

    <script type="text/javascript">
    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
    </script>
    <script type="text/javascript" src="yum.js"></script>
    
  2. Попытка запросить разрешение изатем включение файла динамически

    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
    var script = document.createElement("script");
    script.type="text/javascript";
    script.src="yum.js";
    document.getElementsByTagName("head")[0].appendChild(script);
    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...