Javascript.RU

Конструкции языка. Обработка ошибок.

Update: Более новый материал по этой теме находится по адресу https://learn.javascript.ru/while-for.

Раздел иллюстрирует особенности управляющих конструкций 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 через них нельзя Sad

На метку внешнего цикла можно перейти при помощи 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, а более развернуто:

obj_size

(Более подробно об определении и свойствах объекта написано в разделе Объекты, ООП)

Если мы завернем любые операторы в блок 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)
	}
}

Здесь переменные из разных областей видимости используются в одном выражении.

with javascript

Область видимости size находится внутри obj, поэтому интерпретатор будет искать weight так:

  1. сначала посмотрит weight в size
  2. не найдет там такой переменной
  3. посмотрит weight в obj
  4. использует weight из obj

Работа с with(obj) не эквивалентна работе напрямую с объектом obj, т.к никаких новых переменных добавить в obj нельзя.

Например, сделаем тестовый объект:

obj = {
	weight: 100
}

mwsnap010.jpg

Внутри with можно получить weight, и даже поменять его значение:

javascript with

Но вот при попытке добавить переменную возникнет проблема:

mwsnap012.jpg

Сам объект не поменялся, зато перезаписали глобальную переменную

alert(obj.size)   // => undefined
alert(window.size)   // => 10

У with есть одна большая проблема, из-за которой многие вообще рекомендуют забыть эту конструкцию.

А именно - внутри блока with абсолютно нельзя сказать, откуда будет взята та или иная переменная.

Выше разобран пример, когда weight ищется сначала в size, затем в obj, и потом - в глобальной области window.
Если переменной не окажется там, где она по плану должна быть, то она будет взята/изменена в другой области видимости, возможно даже в глобальной window.

Это приводит к трудноуловимым ошибкам, поэтому часто используют альтернативу: временную переменную.

mwsnap013.jpg

С другой стороны, синтаксис 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, и туда удобно ставить всякие очистки, уведомления о конце процесса и т.п.


Автор: romario (не зарегистрирован), дата: 3 октября, 2008 - 12:16
#permalink

Вопрос к гуру JavaScript: можно ли как-то перехватывать синтаксические ошибки (например, отсутствие скобки какой-нибудь) с помощью try-catch? По умолчанию в IE7 такие ошибки в самом скрипте не перехватываются почему-то, а просто IE выдает свой алерт.


Автор: Илья Кантор, дата: 4 октября, 2008 - 17:30
#permalink

Если скрипт eval'ится - то просто try/catch, а если скрипт загружается с сервера, например, тегом script - и в нем может быть ошибка, то попробуй отловить ее обработчиком window.onerror


Автор: putana (не зарегистрирован), дата: 7 октября, 2008 - 14:31
#permalink

Если надо проерить код на ошибки то
воспользуйся http://jslint.com


Автор: Илья Кантор, дата: 8 октября, 2008 - 09:00
#permalink

Там валидатор вроде очень придирчив к синтаксису, выдает ошибки даже на хорошем коде, не?


Автор: Dmc (не зарегистрирован), дата: 7 декабря, 2008 - 18:40
#permalink

Да, придирчив, часто видит ошибки там где их нет и не видит - там где есть.
Но кое-что всё-таки вылавливает, поэтому я им пользуюсь.

PS. Рад что мои строки перевода drupal с drupaler'а столетней давности используются на подобных проектах


Автор: Гость (не зарегистрирован), дата: 27 апреля, 2012 - 10:38
#permalink

ну как сказать, эта придирчивость настраивается, я им уже давно крашу свои скрипты до идеала)


Автор: Артём_ (не зарегистрирован), дата: 22 января, 2009 - 16:31
#permalink

тема достаточно сложная, но расписана очень понятна


Автор: Enoch Root (не зарегистрирован), дата: 7 июля, 2009 - 16:13
#permalink

А можно ли как-то устанавливать таймаут для операций с try-catch? Например, когда используется скрипт от Google Analitycs, а у человека в данный момент нет доступа во внешний интернет? (сайт виден в локальной сети).


Автор: Илья Кантор, дата: 7 июля, 2009 - 21:22
#permalink

Можно выделить операцию отдельно при помощи setTimeout


Автор: Гость (не зарегистрирован), дата: 20 ноября, 2009 - 03:51
#permalink

То что "Метки действуют только для циклов и switch, т.е организовать полноценный goto через них нельзя" это радостное событие, т.к. "полноценный goto" при неправильном использовании делает программу неуправляемой.

А неправильное использование goto - это, банально, непредсказуемость того, в какую точку программы будет передано управление.


Автор: Гость (не зарегистрирован), дата: 8 октября, 2010 - 10:44
#permalink

Здравсвуйте,
меня интересует сказывается ли использование инструкции With на производительности кода??

или более предпочтительно для сокращения кода использовать переменную (s= obj.size) ??

ЗЫ: спасибо за ресурс =)


Автор: Гость (не зарегистрирован), дата: 14 октября, 2010 - 18:40
#permalink

Привет!

Метки действуют только для циклов и switch, т.е организовать полноценный goto через них нельзя Sad

Это ж клево, что нельзя - нам ведь не нужен спагетти-код;)


Автор: Гость (не зарегистрирован), дата: 14 октября, 2010 - 19:03
#permalink

По поводу switch и использование в нем объектов:
Врядли конечно такое пригодится (так как сравнение для неэлементарных типов осуществляется по ссылке), разве только для проверки, не подменил ли кто объект, на который ссылается ссылка.


Автор: Гость (не зарегистрирован), дата: 30 октября, 2011 - 18:20
#permalink
w=window.open(url);

как в скрипте главного окна отследить, что страница не загрузилась?
(нет страницы, отсутствует соединение....)


Автор: Раед, дата: 3 февраля, 2012 - 19:23
#permalink

так и не разобрался с этими исключениями-ошибками. Например:

throw new Error('my error')

и куда всё это полетит, а главное, для чего может использоваться


Автор: nSauk, дата: 29 августа, 2012 - 15:09
#permalink
for(key in obj) {
  ... obj[key]
}

Недавно познакомился с этим видом циклов и сразу заинтересовался производительностью. Я думал, что a in b — что-то вроде обёртки для a < b.length; a++, но, как оказалось, такие циклы работают быстрее на 10–20% в Firefox и Chrome (в других не проверял). Имейте в виду.


Автор: Фёдор Геращенко (не зарегистрирован), дата: 10 апреля, 2013 - 12:57
#permalink

Внутри with можно получить weight, и даже поменять его значение - внутри "obj" и далее по тексту...


Автор: Гость (не зарегистрирован), дата: 3 июля, 2013 - 14:20
#permalink

Здравствуйте, при установке новой версии Mozilla Firefox, исполняемый файл Firefox Setup 22.0.exe, у меня возникла проблема, стало выдавать ошибку Error: illegal operation on WrappedNative prototype object. Какая причина этой ошибки может быть, как ее исправить? Подскажите пожалуйста. Я не программист, но если Вы мне подскажите, что нужно прочесть по теме, чтобы понять алгоритм действия или алгоритм действия, то мне уже будет легче. Заранее благодарна. С Уважением Ксюша.


Автор: Гость (не зарегистрирован), дата: 14 октября, 2014 - 20:57
#permalink

Про исключения и Error вообще непонятно о чём речь, рассчитано похоже на продвинутых.


Отправить комментарий

Приветствуются комментарии:
  • Полезные.
  • Дополняющие прочитанное.
  • Вопросы по прочитанному. Именно по прочитанному, чтобы ответ на него помог другим разобраться в предмете статьи. Другие вопросы могут быть удалены.
    Для остальных вопросов и обсуждений есть форум.
P.S. Лучшее "спасибо" - не комментарий, как все здорово, а рекомендация или ссылка на статью.
Содержание этого поля является приватным и не предназначено к показу.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Разрешены HTML-таги: <strike> <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <u> <i> <b> <pre> <img> <abbr> <blockquote> <h1> <h2> <h3> <h4> <h5> <p> <div> <span> <sub> <sup>
  • Строки и параграфы переносятся автоматически.
  • Текстовые смайлы будут заменены на графические.

Подробнее о форматировании

CAPTCHA
Антиспам
2 + 11 =
Введите результат. Например, для 1+3, введите 4.
 
Текущий раздел
Поиск по сайту
Реклама
Содержание

Учебник javascript

Основные элементы языка

Сундучок с инструментами

Интерфейсы

Все об AJAX

Оптимизация

Разное

Дерево всех статей

Последние комментарии
Последние темы на форуме
Forum