Конструкции языка. Обработка ошибок.
Раздел иллюстрирует особенности управляющих конструкций javascript, таких как while/for , switch и обработку исключений.
Две формы while:
// проверка вначале
while(i<5) { … }
// условие потом
do { … } while (i<5)
И две принципиально разные формы for . Одна - обычная:
// заметьте - новая переменная объявлена тут же
for (var i=0;i<10;i++) {
...
}
Другая - итерация по свойствам объекта:
for(key in obj) {
... obj[key]
}
Можно исключить унаследованные свойства (т.е исключить из цикла прототип). Для этого используем функцию hasOwnProperty :
// цикл только по собственным свойствам
for(var key in obj) {
if (obj.hasOwnProperty(key)) {
… obj[key] …
}
}
В javascript можно использовать метки, чтобы прыгать между уровнями цикла.
Метки действуют только для циклов и switch , т.е организовать полноценный goto через них нельзя
На метку внешнего цикла можно перейти при помощи break /continue , вот пример:
// внешний цикл
outer: for(…) {
…
// внутренний цикл
for (…) {
…
// можно вывалиться из внешнего цикла
break outer;
…
// или перейти на следующую итерацию внешнего цикла
continue outer;
…
}
}
В некоторых языках можно сделать только switch(число/строка) .
В javascript же конструкция switch принимает совершенно любой объект. Перед switch , как и перед циклами можно ставить метку, и делать break /continue на внешнюю метку.
switch (obj) {
case "test":
…
break
case 5:
…
break outer
default:
…
}
With - специфическая конструкция javascript, самый известный ее аналог - with в Pascal/Delphi. Поэтому мы остановимся на ней подольше.
У каждой функции есть своя область видимости. В простейшем случае, одна функция не видит внутренние переменные другой. Умение обращаться с областью видимости позволяет задавать приватные переменные класса, понимать и создавать замыкания , и многое другое.
Более подробно тема видимости разобрана в статье функции Javascript, а здесь - разберем конкретно то, что с ней делает with .
Коротко - with берет объект и делает из него новую вложенную область видимости.
Например, в некоторой функции:
function go() {
...
var obj
// obj будет браться из текущей области видимости
obj.size = 5
...
}
Создадим тестовый объект, но не просто var obj , а более развернуто:
(Более подробно об определении и свойствах объекта написано в разделе Объекты, ООП)
Если мы завернем любые операторы в блок with(obj) , то все свойства будут первым делом искаться в obj, а уже потом - во внешней области видимости.
В этом примере свойства weight и size будут взяты из объекта obj:
with (obj) {
// width/height берутся из найденного size
return weight / (size.width + size.height)
}
А здесь - with делает область видимости из obj.size :
with (obj.size) {
return width + height
}
В чем польза with? Как правило, его используют для упрощения синтаксиса, чтобы много раз не повторять объект.
Типичный пример из реальной жизни:
with (div.style) {
if (!browser.isSafari) {
position = 'absolute'
}
left = 0
right = 0
visibility = 'hidden'
}
Напомню, что интерпретатор javascript ищет все переменные в ближайшей области видимости. Если не находит - ищет в следующей, и так далее - до глобальных переменных, т.е window.
Поэтому можно, например, делать конструкции типа такой:
with (obj) {
with (size) {
return weight / (width + height)
}
}
Здесь переменные из разных областей видимости используются в одном выражении.
Область видимости size находится внутри obj , поэтому интерпретатор будет искать weight так:
- сначала посмотрит
weight в size
- не найдет там такой переменной
- посмотрит
weight в obj
- использует
weight из obj
Работа с with(obj) не эквивалентна работе напрямую с объектом obj , т.к никаких новых переменных добавить в obj нельзя.
Например, сделаем тестовый объект:
obj = {
weight: 100
}
Внутри with можно получить weight, и даже поменять его значение:
Но вот при попытке добавить переменную возникнет проблема:
Сам объект не поменялся, зато перезаписали глобальную переменную
alert(obj.size) // => undefined
alert(window.size) // => 10
У with есть одна большая проблема, из-за которой многие вообще рекомендуют забыть эту конструкцию.
А именно - внутри блока with абсолютно нельзя сказать, откуда будет взята та или иная переменная.
Выше разобран пример, когда weight ищется сначала в size , затем в obj , и потом - в глобальной области window .
Если переменной не окажется там, где она по плану должна быть, то она будет взята/изменена в другой области видимости, возможно даже в глобальной window .
Это приводит к трудноуловимым ошибкам, поэтому часто используют альтернативу: временную переменную.
С другой стороны, синтаксис with удобнее и полезен там, где ошибок уж точно не может быть. Например, при манипуляции стилями.
Работа с исключениями в javascript организована в типичном для языка стиле вседозволенности.
Абсолютно любой объект можно бросить в виде исключения:
try {
...
throw {message: "Ого!"}
..
} catch (e) {
alert("Ага, попался!")
}
Обычно бросают, все же, не простые объекты, а потомки встроенного класса Error :
throw new Error("connection down, server timeout")
// или сделать наследника Error:
throw new ConnectionError("server timeout")
Встроенный класс Error удобен для унификации проверки "a instanceof Error " и интероперабельности с другими библиотеками, так как ничего полезного в нем часто нет.
В Firefox у объекта Error есть полный бэктрейс в свойстве stack и еще масса полезных свойств:
// так можно быстро узнать стек без отладчика
alert( (new Error()).stack )
В IE тоже есть некоторые полезные свойства, а в Opera/Safari - на момент написания только message :
var e = new Error("hi")
alert(e.message) // => "hi"
Поэтому, например, у меня созданы потомки (синтаксис фреймворка dojo):
// базовый класс для ошибок, со стеком (Firefox)
// сообщением message и дополнительной информацией extra
dojo.declare(
"dojo.Error",
Error,
function(message, extra) {
this.message = message;
this.extra = extra;
this.stack = (new Error()).stack;
}
)
// Потомок, имя ошибки хранится в name
// формат: dojo.declare(Имя, Родитель, Конструктор)
dojo.declare(
"dojo.CommunicationError",
dojo.Error,
function() {
this.name="CommunicationError";
}
)
// еще потомок для примера
dojo.declare(
"dojo.LockedError",
dojo.Error,
function() {
this.name="LockedError";
}
)
...
При перехвате исключений редко нужно перехватывать все подряд. Обычно - надо перехватить определенный класс исключений. Стандартный оператор catch такого не умеет, поэтому полный код обработки будет выглядить так:
try {
… код ...
} catch(e) {
// ловим нужное исключение
if (e instanceof ConnectionError) {
// обрабатываем его
… reconnect …
} else {
// пробрасываем незнакомое исключение дальше
throw e
}
} finally {
// блок finally выполняется всегда,
// вне зависимости - было исключение или нет
… notifyUser() ..
}
В этом примере также присутствует блок finally , взятый в javascript из java. В стандартной схеме try..catch..finally, код из блока finally выполняется либо после try , если эксепшна нет, либо после catch , даже если эксепшн выпал наружу.
Короче говоря, код в finally выполнится при любом результате работы try/catch , и туда удобно ставить всякие очистки, уведомления о конце процесса и т.п.
|
Вопрос к гуру JavaScript: можно ли как-то перехватывать синтаксические ошибки (например, отсутствие скобки какой-нибудь) с помощью try-catch? По умолчанию в IE7 такие ошибки в самом скрипте не перехватываются почему-то, а просто IE выдает свой алерт.
Если скрипт
eval
'ится - то простоtry/catch
, а если скрипт загружается с сервера, например, тегомscript
- и в нем может быть ошибка, то попробуй отловить ее обработчикомwindow.onerror
Если надо проерить код на ошибки то
воспользуйся http://jslint.com
Там валидатор вроде очень придирчив к синтаксису, выдает ошибки даже на хорошем коде, не?
Да, придирчив, часто видит ошибки там где их нет и не видит - там где есть.
Но кое-что всё-таки вылавливает, поэтому я им пользуюсь.
PS. Рад что мои строки перевода drupal с drupaler'а столетней давности используются на подобных проектах
ну как сказать, эта придирчивость настраивается, я им уже давно крашу свои скрипты до идеала)
тема достаточно сложная, но расписана очень понятна
А можно ли как-то устанавливать таймаут для операций с try-catch? Например, когда используется скрипт от Google Analitycs, а у человека в данный момент нет доступа во внешний интернет? (сайт виден в локальной сети).
Можно выделить операцию отдельно при помощи setTimeout
То что "Метки действуют только для циклов и switch, т.е организовать полноценный goto через них нельзя" это радостное событие, т.к. "полноценный goto" при неправильном использовании делает программу неуправляемой.
А неправильное использование goto - это, банально, непредсказуемость того, в какую точку программы будет передано управление.
Здравсвуйте,
меня интересует сказывается ли использование инструкции With на производительности кода??
или более предпочтительно для сокращения кода использовать переменную (s= obj.size) ??
ЗЫ: спасибо за ресурс =)
Привет!
Метки действуют только для циклов и switch, т.е организовать полноценный goto через них нельзя Sad
Это ж клево, что нельзя - нам ведь не нужен спагетти-код;)
По поводу switch и использование в нем объектов:
Врядли конечно такое пригодится (так как сравнение для неэлементарных типов осуществляется по ссылке), разве только для проверки, не подменил ли кто объект, на который ссылается ссылка.
как в скрипте главного окна отследить, что страница не загрузилась?
(нет страницы, отсутствует соединение....)
так и не разобрался с этими исключениями-ошибками. Например:
и куда всё это полетит, а главное, для чего может использоваться
Недавно познакомился с этим видом циклов и сразу заинтересовался производительностью. Я думал, что
a in b
— что-то вроде обёртки дляa < b.length; a++
, но, как оказалось, такие циклы работают быстрее на 10–20% в Firefox и Chrome (в других не проверял). Имейте в виду.Внутри with можно получить weight, и даже поменять его значение - внутри "obj" и далее по тексту...
Здравствуйте, при установке новой версии Mozilla Firefox, исполняемый файл Firefox Setup 22.0.exe, у меня возникла проблема, стало выдавать ошибку Error: illegal operation on WrappedNative prototype object. Какая причина этой ошибки может быть, как ее исправить? Подскажите пожалуйста. Я не программист, но если Вы мне подскажите, что нужно прочесть по теме, чтобы понять алгоритм действия или алгоритм действия, то мне уже будет легче. Заранее благодарна. С Уважением Ксюша.
меня возникла проблема, стало выдавать ошибку Error: illegal operation on WrappedNative prototype object. Какая причина этой ошибки может быть, как ее исправить? Подскажите пожалуйста. Я не п
Про исключения и Error вообще непонятно о чём речь, рассчитано похоже на продвинутых.
Первая форма while - это цикл с предусловием, где условие проверяется в начале перед выполнением блока кода. Если условие истинно, блок кода выполняется, а затем проверяется условие снова. Если условие ложно, цикл прекращается и выполнение продолжается со следующей инструкции после цикла.
Вторая форма do-while - это цикл с постусловием, где блок кода выполняется сначала, а затем проверяется условие. Если условие истинно, цикл продолжается и блок кода выполняется снова. Если условие ложно, цикл прекращается и выполнение продолжается со следующей инструкции после цикла.
Обе формы цикла while могут использоваться для повторения блока кода до тех пор, пока определенное условие остается истинным.