Бит опоздал на вечеринку, но я изучал этот вопрос сегодня и заметил, что многие ответы не полностью касаются того, как Javascript обрабатывает области, что по существу то, что это сводится к.
So как упоминалось многими другими, проблема в том, что внутренняя функция ссылается на одну и ту же переменную i
. Итак, почему бы нам просто не создать новую локальную переменную на каждой итерации и вместо этого иметь ссылку на внутреннюю функцию?
//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML += '' + msg + '
';};
var funcs = {};
for (var i = 0; i < 3; i++) {
var ilocal = i; //create a new local variable
funcs[i] = function() {
console.log("My value: " + ilocal); //each should reference its own local variable
};
}
for (var j = 0; j < 3; j++) {
funcs[j]();
}
Точно так же раньше, когда каждая внутренняя функция выдавала последнее значение, присвоенное i
, теперь каждая внутренняя функция просто выводит последнее значение, назначенное на ilocal
. Но не должна ли каждая итерация иметь свою собственную ilocal
?
Оказывается, в этом и проблема. Каждая итерация разделяет одну и ту же область, поэтому каждая итерация после первого просто переписывается ilocal
. Из MDN :
Важно: JavaScript не имеет области блока. Переменные, введенные с блоком, привязаны к содержащейся функции или скрипту, а эффекты их настройки сохраняются за пределами самого блока. Другими словами, операторы блоков не вводят область. Хотя «автономные» блоки являются допустимым синтаксисом, вы не хотите использовать автономные блоки в JavaScript, потому что они не делают то, что, по вашему мнению, они делают, если вы думаете, что они делают что-то вроде таких блоков на C или Java.
blockquote>Повторяется для акцента:
JavaScript не имеет области блока. Переменные, введенные с блоком, привязаны к содержащейся функции или скрипту
blockquote>. Мы можем увидеть это, проверив
ilocal
, прежде чем объявить его на каждой итерации:
//overwrite console.log() so you can see the console output console.log = function(msg) {document.body.innerHTML += '
' + msg + '
';}; var funcs = {}; for (var i = 0; i < 3; i++) { console.log(ilocal); var ilocal = i; }Именно поэтому эта ошибка настолько сложна. Несмотря на то, что вы обновляете переменную, Javascript не будет вызывать ошибку, и JSLint даже не выдаст предупреждение. Именно поэтому лучший способ решить эту проблему - воспользоваться преимуществами закрытия, что по сути является идеей, что в Javascript внутренние функции имеют доступ к внешним переменным, потому что внутренние области «заключают» внешние области.
[/g13]
Это также означает, что внутренние функции «удерживают» внешние переменные и сохраняют их, даже если внешняя функция возвращается. Чтобы использовать это, мы создаем и вызываем функцию-оболочку только для создания новой области, объявляем
ilocal
в новой области и возвращаем внутреннюю функцию, которая используетilocal
(более подробное объяснение ниже):
//overwrite console.log() so you can see the console output console.log = function(msg) {document.body.innerHTML += '
' + msg + '
';}; var funcs = {}; for (var i = 0; i < 3; i++) { funcs[i] = (function() { //create a new scope using a wrapper function var ilocal = i; //capture i into a local var return function() { //return the inner function console.log("My value: " + ilocal); }; })(); //remember to run the wrapper function } for (var j = 0; j < 3; j++) { funcs[j](); }Создание внутренней функции внутри функции-обертки дает внутренней функции частную среду, доступ к которой может получить только «закрытие». Таким образом, каждый раз, когда мы вызываем функцию-оболочку, мы создаем новую внутреннюю функцию с ее собственной отдельной средой, гарантируя, что переменные
ilocal
не сталкиваются и не перезаписывают друг друга. Несколько незначительных оптимизаций дают окончательный ответ, который дали многие другие пользователи SO:
//overwrite console.log() so you can see the console output console.log = function(msg) {document.body.innerHTML += '
' + msg + '
';}; var funcs = {}; for (var i = 0; i < 3; i++) { funcs[i] = wrapper(i); } for (var j = 0; j < 3; j++) { funcs[j](); } //creates a separate environment for the inner function function wrapper(ilocal) { return function() { //return the inner function console.log("My value: " + ilocal); }; }Обновить
С ES6 теперь mainstream, теперь мы можем использовать новое ключевое слово
let
для создания переменных с блочным диапазоном:
//overwrite console.log() so you can see the console output console.log = function(msg) {document.body.innerHTML += '
' + msg + '
';}; var funcs = {}; for (let i = 0; i < 3; i++) { // use "let" to declare "i" funcs[i] = function() { console.log("My value: " + i); //each should reference its own local variable }; } for (var j = 0; j < 3; j++) { // we can use "var" here without issue funcs[j](); }Посмотрите, как легко это сейчас! Для получения дополнительной информации см. этот ответ , на котором основана моя информация.
Когда вы сталкиваетесь с «странным» исключением, указывающим на то, что классы / методы / файлы / компоненты / теги отсутствуют или различаются, хотя они явно включены в веб-приложение, такое как приведенные ниже,
java.lang.ClassFormatError: Отсутствует атрибут кода в методе, который не является родным или абстрактным в файле класса javax / faces / webapp / FacesServlet
blockquote>
java.util.MissingResourceException: Не удается найти javax.faces.LogStrings bundle
blockquote>
com.sun.faces.vendor.WebContainerInjectionProvider не может быть cast to com.sun.faces.spi.InjectionProvider
blockquote>
com.sun.faces.config.ConfigurationException: CONFIGURATION FAILED
blockquote>
Тег с именем inputFile из пространства имен http: & # x2f; /xmlns.jcp.org/jsf/html имеет нулевой класс обработчика.
< / blockquote>
java.lang.NullPoint erException в javax.faces.CurrentThreadToServletContext.getFallbackFactory
blockquote>
java.lang.AbstractMethodError в javax.faces.application.ViewHandlerWrapper.getWebsocketURL
< / blockquote>или когда вы сталкиваетесь с «странным» временем выполнения, таким как сломанные сеансы HTTP (
jsessionid
отображается в URL-адресах по всему месту) и / или сломанная область видимости JSF (она ведет себя как область запроса) и / или сломанные ресурсы CSS / JS / image, тогда вероятность велика, что путь к классу среды выполнения Webapp загрязнен дублированными разными версиями JAR-файлов.В вашем конкретном случае с
ClassFormatError
наFacesServlet
, это означает, что файл JAR, содержащий упомянутый класс, был найден в первый раз, фактически является «файлом JAR-файла API», предназначенным для поставщиков решений (таких как разработчики, работающие в Mojarra и MyFaces). Он содержит файлы классов с только сигнатурами классов и методов, без каких-либо структур кода и файлов ресурсов. Именно это означает «отсутствующий атрибут кода».Всегда отмечайте серверные библиотеки как
provided
Все зависимости, помеченные как « Спецификации Java » в Maven и имеющие
-api
суффикс в идентификаторе артефакта - эти API-интерфейсы. Их не должно быть в пуле классов. Вы должны всегда отмечать их<scope>provided</scope>
, если вам действительно нужно иметь его в своем пом. Известным примером является API-интерфейс Java EE (Web) :<dependency> <groupId>javax</groupId> <artifactId>javaee-web-api</artifactId> <version><!-- 6.0 or 7.0 or newer --></version> <scope>provided</scope> </dependency>
Если область
provided
отсутствует, то этот JAR окажется в файле/WEB-INF/lib
webapp [ , вызывая все проблемы, с которыми вы сталкиваетесь сейчас. В этом конкретном случае у вас есть ненужная зависимость JSF API :<dependency> <groupId>javax.faces</groupId> <artifactId>javax.faces-api</artifactId> </dependency>
вызывает проблему, потому что в ней содержится класс чертежа
FacesServlet
. Удалите его и опираясь на API-интерфейсprovided
Java EE (Web), как показано выше, должны решить его.Tomcat как баскетбольный контейнер JSP / Servlet уже предоставляет JSP, Servlet и EL (а также 8 WebSocket). Поэтому вы должны отмечать как минимум
jsp-api
,servlet-api
иel-api
какprovided
. Tomcat не предоставляет JSF (и JSTL ). Таким образом, вам нужно будет установить его через webapp.Полноценные серверы Java EE, такие как WildFly, TomEE, GlassFish, Payara, WebSphere и т. Д., Уже предоставляют весь API Java EE, включая JSF , Поэтому вам абсолютно не нужно устанавливать JSF через webapp. Это приведет только к конфликтам, если сервер уже предоставит другую версию и / или версию из коробки. Единственная зависимость, в которой вы нуждаетесь, - это
javaee-web-api
, как показано выше.Установка JSF на Tomcat
. Правильный способ установки JSF в Tomcat упоминается в нашей JSF wiki - Установка JSF . Существует 2 реализации JSF, Mojarra и MyFaces . Вы должны выбрать один из них и, следовательно, не оба.
Установка Mojarra на Tomcat:
<dependency> <groupId>org.glassfish</groupId> <artifactId>javax.faces</artifactId> <version><!-- Check https://javaserverfaces.github.io --></version> </dependency>
Вы также можете проверить
org.glassfish:javax.faces
репозиторий для текущей последней версии (которая в настоящее время2.2.13
). См. Также собственные инструкции по установке Mojarra .Установка MyFaces на Tomcat:
<dependency> <groupId>org.apache.myfaces.core</groupId> <artifactId>myfaces-api</artifactId> <version><!-- Check http://myfaces.apache.org --></version> </dependency> <dependency> <groupId>org.apache.myfaces.core</groupId> <artifactId>myfaces-impl</artifactId> <version><!-- Check http://myfaces.apache.org --></version> </dependency>
Вы также можете проверить репозиторий
org.apache.myfaces.core:myfaces-bundle
для текущей версии последней версии (в настоящее время2.2.10
).Обратите внимание, что Tomcat 6 как контейнер Servlet 2.5 поддерживает максимальный JSF 2.1. Не забудьте также установить JSTL. См. Также наш JSF wiki - Установка JSF .
См. Также: