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. Какая причина этой ошибки может быть, как ее исправить? Подскажите пожалуйста. Я не программист, но если Вы мне подскажите, что нужно прочесть по теме, чтобы понять алгоритм действия или алгоритм действия, то мне уже будет легче. Заранее благодарна. С Уважением Ксюша.


Автор: find this (не зарегистрирован), дата: 14 февраля, 2020 - 11:19
#permalink

меня возникла проблема, стало выдавать ошибку Error: illegal operation on WrappedNative prototype object. Какая причина этой ошибки может быть, как ее исправить? Подскажите пожалуйста. Я не п


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

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


Автор: Гость (не зарегистрирован), дата: 21 мая, 2023 - 02:42
#permalink

Первая форма while - это цикл с предусловием, где условие проверяется в начале перед выполнением блока кода. Если условие истинно, блок кода выполняется, а затем проверяется условие снова. Если условие ложно, цикл прекращается и выполнение продолжается со следующей инструкции после цикла.

Вторая форма do-while - это цикл с постусловием, где блок кода выполняется сначала, а затем проверяется условие. Если условие истинно, цикл продолжается и блок кода выполняется снова. Если условие ложно, цикл прекращается и выполнение продолжается со следующей инструкции после цикла.

Обе формы цикла while могут использоваться для повторения блока кода до тех пор, пока определенное условие остается истинным.


 
Текущий раздел
Поиск по сайту
Содержание

Учебник javascript

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

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

Интерфейсы

Все об AJAX

Оптимизация

Разное

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

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