final cleanup regexps

This commit is contained in:
Ilya Kantor 2015-04-07 15:22:06 +03:00
parent 59388d093e
commit 833f7ba70e
132 changed files with 410 additions and 183 deletions

View file

@ -210,13 +210,13 @@ Adobe Flash -- кросс-браузерная платформа для мул
<ul> <ul>
<li>Язык [CoffeeScript](http://coffeescript.org/) -- это "синтаксический сахар" поверх JavaScript, он сосредоточен на большей ясности и краткости кода. Как правило, его особенно любят программисты на Ruby.</li> <li>Язык [CoffeeScript](http://coffeescript.org/) -- это "синтаксический сахар" поверх JavaScript, он сосредоточен на большей ясности и краткости кода. Как правило, его особенно любят программисты на Ruby.</li>
<li>Язык [TypeScript](http://www.typescriptlang.org/) сосредоточен на добавлении строгой типизации данных, он предназначен для упрощения разработки и поддержки больших систем. Его разрабатывает MicroSoft.</li> <li>Язык [TypeScript](http://www.typescriptlang.org/) сосредоточен на добавлении строгой типизации данных, он предназначен для упрощения разработки и поддержки больших систем. Его разрабатывает MicroSoft.</li>
<li>Язык [Dart](https://www.dartlang.org/) предложен компанией Google как замена JavaScript, но другие ведущие интернет-компании объявили о своей незаинтересованности в Dart. Возможно, в будущем он может составить конкуренцию JS.</li> <li>Язык [Dart](https://www.dartlang.org/) интересен тем, что он не только транслируется в JavaScript, как и другие языки, но и имеет свою независимую среду выполнения, которая даёт ему ряд возможностей и доступна для встраивания в приложения (вне браузера). Он разрабатывается компанией Google.</li>
</ul> </ul>
[smart header="ES6 и ES7 прямо сейчас"] [smart header="ES6 и ES7 прямо сейчас"]
Существуют также трансляторы, которые берут код, использующий возможности будущих стандартов JavaScript, и преобразуют его в более старый вариант, который понимают все браузеры. Существуют также трансляторы, которые берут код, использующий возможности будущих стандартов JavaScript, и преобразуют его в более старый вариант, который понимают все браузеры.
Например, [6to5](https://6to5.org/). Например, [babeljs](https://babeljs.io/).
Благодаря этому, мы можем использовать многие возможности будущего уже сегодня. Благодаря этому, мы можем использовать многие возможности будущего уже сегодня.
[/smart] [/smart]

View file

@ -268,72 +268,6 @@ function showWarning(width, height, title, contents) {
} }
``` ```
### "Именованные аргументы"
*Именованные аргументы* -- альтернативная техника работы с аргументами, которая вообще не использует `arguments`.
Некоторые языки программирования позволяют передать параметры как-то так: `f(width=100, height=200)`, то есть по именам, а что не передано, тех аргументов нет. Это очень удобно в тех случаях, когда аргументов много, сложно запомнить их порядок и большинство вообще не надо передавать, по умолчанию подойдёт.
Такая ситуация часто встречается в компонентах интерфейса. Например, у "меню" может быть масса настроек отображения, которые можно "подкрутить" но обычно нужно передать всего один-два главных параметра, а остальные возьмутся по умолчанию.
В JavaScript для этих целей используется передача аргументов в виде объекта, а в его свойствах мы передаём параметры.
Получается так:
```js
function showWarning(options) {
var width = options.width || 200; // по умолчанию
var height = options.height || 100;
var title = options.title || "Предупреждение";
// ...
}
showWarning({
```
Вызвать такую функцию очень легко. Достаточно передать объект аргументов, указав в нем только нужные:
```js
showWarning({
contents: "Вы вызвали функцию" // и всё понятно!
});
```
Сравним это с передачей аргументов через список:
```js
showWarning(null, null, "Предупреждение!");
// мысль программиста "а что это за null, null в начале? ох, надо глядеть описание функции"
```
Не правда ли, объект -- гораздо проще и понятнее?
Еще один бонус кроме красивой записи -- возможность повторного использования объекта аргументов:
```js
var opts = {
width: 400,
height: 200,
contents: "Текст"
};
showWarning(opts);
opts.contents = "Другой текст";
*!*
showWarning(opts); // вызвать с новым текстом, без копирования других аргументов
*/!*
```
Именованные аргументы применяются во многих JavaScript-фреймворках.
## Устаревшее свойство arguments.callee [#arguments-callee] ## Устаревшее свойство arguments.callee [#arguments-callee]
[warn header="Используйте NFE вместо `arguments.callee`"] [warn header="Используйте NFE вместо `arguments.callee`"]
@ -407,6 +341,72 @@ function f3() {
В учебнике мы это свойство также не будем использовать. В учебнике мы это свойство также не будем использовать.
## "Именованные аргументы"
*Именованные аргументы* -- альтернативная техника работы с аргументами, которая вообще не использует `arguments`.
Некоторые языки программирования позволяют передать параметры как-то так: `f(width=100, height=200)`, то есть по именам, а что не передано, тех аргументов нет. Это очень удобно в тех случаях, когда аргументов много, сложно запомнить их порядок и большинство вообще не надо передавать, по умолчанию подойдёт.
Такая ситуация часто встречается в компонентах интерфейса. Например, у "меню" может быть масса настроек отображения, которые можно "подкрутить" но обычно нужно передать всего один-два главных параметра, а остальные возьмутся по умолчанию.
В JavaScript для этих целей используется передача аргументов в виде объекта, а в его свойствах мы передаём параметры.
Получается так:
```js
function showWarning(options) {
var width = options.width || 200; // по умолчанию
var height = options.height || 100;
var title = options.title || "Предупреждение";
// ...
}
showWarning({
```
Вызвать такую функцию очень легко. Достаточно передать объект аргументов, указав в нем только нужные:
```js
showWarning({
contents: "Вы вызвали функцию" // и всё понятно!
});
```
Сравним это с передачей аргументов через список:
```js
showWarning(null, null, "Предупреждение!");
// мысль программиста "а что это за null, null в начале? ох, надо глядеть описание функции"
```
Не правда ли, объект -- гораздо проще и понятнее?
Еще один бонус кроме красивой записи -- возможность повторного использования объекта аргументов:
```js
var opts = {
width: 400,
height: 200,
contents: "Текст"
};
showWarning(opts);
opts.contents = "Другой текст";
*!*
showWarning(opts); // вызвать с новым текстом, без копирования других аргументов
*/!*
```
Именованные аргументы применяются во многих JavaScript-фреймворках.
## Итого ## Итого
<ul> <ul>

View file

@ -354,5 +354,5 @@ function Rabbit() {
...Которой нет в прототипном подходе, потому что в процессе создания `new Rabbit` мы вовсе не обязаны вызывать конструктор родителя. Ведь методы находятся в прототипе. ...Которой нет в прототипном подходе, потому что в процессе создания `new Rabbit` мы вовсе не обязаны вызывать конструктор родителя. Ведь методы находятся в прототипе.
Поэтому прототипный подход стоит предпочитать функциональному как более быстрый и универсальный. А что касается красоты синтаксиса -- она сильно лучше в новом стандарте ES6, которым можно пользоваться уже сейчас, если взять транслятор [6to5](http://6to5.org/). Поэтому прототипный подход стоит предпочитать функциональному как более быстрый и универсальный. А что касается красоты синтаксиса -- она сильно лучше в новом стандарте ES6, которым можно пользоваться уже сейчас, если взять транслятор [babeljs](https://babeljs.io/).

View file

@ -10,7 +10,7 @@
<text id="jump:-function" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal"> <text id="jump:-function" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-weight="normal">
<tspan x="24" y="175" fill="#8A704D">jump: function</tspan> <tspan x="24" y="175" fill="#8A704D">jump: function</tspan>
<tspan x="131.761719" y="175" fill="#999647"></tspan> <tspan x="131.761719" y="175" fill="#999647"></tspan>
<tspan x="24" y="191" fill="#7ED321">run: function</tspan> <tspan x="24" y="191" fill="#417505">run: function</tspan>
</text> </text>
<text id="Rabbit.prototype" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-style="italic" font-weight="normal" fill="#8A704D"> <text id="Rabbit.prototype" sketch:type="MSTextLayer" font-family="Consolas" font-size="14" font-style="italic" font-weight="normal" fill="#8A704D">
<tspan x="13" y="149">Rabbit.prototype</tspan> <tspan x="13" y="149">Rabbit.prototype</tspan>

Before

Width:  |  Height:  |  Size: 4 KiB

After

Width:  |  Height:  |  Size: 4 KiB

Before After
Before After

View file

@ -14,7 +14,7 @@
Но могут быть и другие, например `PropertyError` -- эта ошибка будет возникать, если в прочитанном объекте нет свойства `name` или `age`. Но могут быть и другие, например `PropertyError` -- эта ошибка будет возникать, если в прочитанном объекте нет свойства `name` или `age`.
Реализуем её: Реализуем класс `PropertyError`:
```js ```js
function PropertyError(property) { function PropertyError(property) {
@ -34,17 +34,17 @@ function PropertyError(property) {
PropertyError.prototype = Object.create(Error.prototype); PropertyError.prototype = Object.create(Error.prototype);
``` ```
Посмотрим внимательнее на код -- в нём важные детали того, как можно позаботиться о стандартных свойствах объекта ошибки: В этом коде вы можете видеть ряд важных деталей, важных именно для ошибок:
<dl> <dl>
<dt>`name` -- имя ошибки.</dt> <dt>`name` -- имя ошибки.</dt>
<dd>Должно совпадать с именем функции, просто записали строку в него.</dd> <dd>Должно совпадать с именем функции.</dd>
<dt>`message` -- сообщение об ошибке.</dt> <dt>`message` -- сообщение об ошибке.</dt>
<dd>Несмотря на то, что `PropertyError` наследует от `Error` (последняя строка), конструктор у неё немного другой. Он принимает не сообщение об ошибке, а название свойства `property`, ну а сообщение генерируется из него. <dd>Несмотря на то, что `PropertyError` наследует от `Error` (последняя строка), конструктор у неё немного другой. Он принимает не сообщение об ошибке, а название свойства `property`, ну а сообщение генерируется из него.
В результате в ошибке есть как стандартное свойство `message`, так и более точное `property`. В результате в объекте ошибки есть как стандартное свойство `message`, так и более точное `property`.
Это полезная практика -- добавлять в объект ошибки свойства, более подробно описывающие ситуацию, которых нет в базовых объектах `Error`.</dd> Это частая практика -- добавлять в объект ошибки свойства, которых нет в базовых объектах `Error`, более подробно описывающие ситуацию для данного класса ошибок.</dd>
<dt>`stack` -- стек вызовов, которые в итоге привели к ошибке.</dt> <dt>`stack` -- стек вызовов, которые в итоге привели к ошибке.</dt>
<dd>У встроенных объектов `Error` это свойство есть автоматически, вот к примеру: <dd>У встроенных объектов `Error` это свойство есть автоматически, вот к примеру:
```js ```js
@ -53,30 +53,28 @@ function f() {
alert( new Error().stack ); alert( new Error().stack );
} }
f(); f(); // выведет список вложенных вызовов, с номерами строк, где они были сделаны
``` ```
Если же объект делаем мы, то "по умолчанию" такого свойства у него не будет. Нам нужно как-то самим узнавать последовательность вложенных вызовов на текущий момент. Однако удобного способа сделать это в JavaScript нет, поэтому мы поступаем хитро и копируем его из нового объекта `new Error`, который генерируем тут же. Если же объект ошибки делаем мы, то "по умолчанию" у него такого свойства у него не будет. Нам нужно как-то самим узнавать последовательность вложенных вызовов на текущий момент. Однако удобного способа сделать это в JavaScript нет, поэтому мы поступаем хитро и копируем его из нового объекта `new Error`, который генерируем тут же.
В V8 (Chrome, Opera, Node.JS) есть нестандартное расширение [Error.captureStackTrace](https://code.google.com/p/v8-wiki/wiki/JavaScriptStackTraceApi), которое позволяет стек получать. В V8 (Chrome, Opera, Node.JS) есть нестандартное расширение [Error.captureStackTrace](https://code.google.com/p/v8-wiki/wiki/JavaScriptStackTraceApi), которое позволяет стек получать.
Строка из кода выше: Это делает строка из кода выше:
```js ```js
Error.captureStackTrace(this, PropertyError); Error.captureStackTrace(this, PropertyError);
``` ```
Вызов записывает в объект `this` (текущий объект ошибки) стек вызовов, а второй аргумент -- это текущий конструктор, он не обязателен, но если есть, то говорит, что при генерации стека нужно на этой функции остановиться. В результате в стеке не будет информации о том, что делалось внутри конструктора `PropertyError`. Такой вызов записывает в объект `this` (текущий объект ошибки) стек вызовов, ну а второй аргумент -- вообще не обязателен, но если есть, то говорит, что при генерации стека нужно на этой функции остановиться. В результате в стеке будет информация о цепочке вложенных вызовов вплоть до вызова `PropertyError`.
То есть, будет последовательность вызовов до генерации ошибки, но не включая код самого конструктора ошибки, который, как правило, не интересен. Такое поведение максимально соответствует встроенным ошибкам JavaScript. То есть, будет последовательность вызовов до генерации ошибки, но не включая код самого конструктора ошибки, который, как правило, не интересен. Такое поведение максимально соответствует встроенным ошибкам JavaScript.
</dd> </dd>
</dl> </dl>
[smart header="Конструктор родителя здесь не нужен"] [smart header="Конструктор родителя здесь не нужен"]
В коде выше не вызывается конструктор родителя. Обычно, когда мы наследуем, то мы вызываем его. Обычно, когда мы наследуем, то вызываем конструктор родителя. В данном случае вызов выглядел бы как `Error.call(this, message)`.
В данном случае вызов выглядел бы как `Error.call(this, message)`. Однако, встроенный конструктор `Error` ничего полезного не делает, даже свойство `this.message` (не говоря уже об `name` и `stack`) не назначает. Поэтому и вызывать его здесь нет необходимости.
Однако, встроенный конструктор `Error` на редкость прост, он ничего полезного не делает, даже свойство `this.message` (не говоря уже об `name` и `stack`) не назначает. Поэтому и вызывать его здесь нет необходимости.
[/smart] [/smart]
@ -93,7 +91,7 @@ function PropertyError(property) {
this.name = "PropertyError"; this.name = "PropertyError";
this.property = property; this.property = property;
this.message = "Отсутствует свойство " + property; this.message = "Ошибка в свойстве " + property;
if (Error.captureStackTrace) { if (Error.captureStackTrace) {
Error.captureStackTrace(this, PropertyError); Error.captureStackTrace(this, PropertyError);
@ -137,49 +135,50 @@ try {
alert( "Здравствуйте, Аноним!" ); alert( "Здравствуйте, Аноним!" );
*/!* */!*
} else { } else {
alert( err.message ); // Отсутствует свойство ... alert( err.message ); // Ошибка в свойстве ...
} }
} else if (err instanceof SyntaxError) { } else if (err instanceof SyntaxError) {
alert( "Ошибка в данных: " + err.message ); alert( "Ошибка в синтаксисе данных: " + err.message );
} else { } else {
throw err; // неизвестная ошибка, не знаю что с ней делать throw err; // неизвестная ошибка, не знаю что с ней делать
} }
} }
``` ```
Обратим внимание на проверку типа ошибки в `try..catch`. Всё работает -- и наша ошибка `PropertyError` и встроенная `SyntaxError` корректно генерируются, перехватываются, обрабатываются.
Оператор `instanceof` поддерживает иерархию. Это значит, что если мы в дальнейшем решим как-то ещё уточнить тип ошибки `PropertyError`, то для объекта, наследующего от него, `e instanceof PropertyError` по-прежнему будет работать. Обратим внимание на проверку типа ошибки в `try..catch`. Оператор `instanceof` проверяет класс с учётом наследования. Это значит, что если мы в дальнейшем решим создать новый тип ошибки, наследующий от `PropertyError`, то проверка `err instanceof PropertyError` для класса-наследника тоже будет работать. Код получился расширяемым, это очень важно.
## Дальнейшее наследование ## Дальнейшее наследование
Чтобы создать иерархию, нужно наследовать от `PropertyError`.
`PropertyError` -- это просто общего вида ошибка в свойстве. Создадим ошибку `PropertyRequiredError`, которая означает, что свойства нет. `PropertyError` -- это просто общего вида ошибка в свойстве. Создадим ошибку `PropertyRequiredError`, которая означает, что свойства нет.
Типичный вид конструктора-наследника -- такой: Эт подвид `PropertyError`, так что унаследуем он неё. Общий вид конструктора-наследника -- стандартный:
```js ```js
function PropertyRequiredError(property) { function PropertyRequiredError(property) {
// вызываем конструктор родителя и передаём текущие аргументы
PropertyError.apply(this, arguments); PropertyError.apply(this, arguments);
... ...
} }
``` ```
Можем ли мы просто вызвать конструктор родителя и ничего не делать в дополнение? Увы, нет. Достаточно ли в наследнике просто вызвать конструктор родителя? Увы, нет.
Если так поступить, то свойство `this.name` будет некорректным, да и `Error.captureStackTrace` тоже получит неправильную функцию вторым параметром. Если так поступить, то свойство `this.name` будет некорректным, да и `Error.captureStackTrace` тоже получит неправильную функцию вторым параметром.
Можно ли как-то поправить конструктор родителя? Убрать из него все упоминания о конкретном классе `PropertyError` и сделать код универсальным? Можно ли как-то поправить конструктор родителя, чтобы от него было проще наследовать?
Частично -- да. Как мы помним, существует свойство `constructor`, которое есть в `prototype` по умолчанию, и которое мы можем намеренно сохранить при наследовании: Для этого нужно убрать из него упоминания о конкретном классе `PropertyError`, чтобы сделать код универсальным. Частично -- это возможно. Как мы помним, существует свойство `constructor`, которое есть в `prototype` по умолчанию, и которое мы можем намеренно сохранить при наследовании.
Исправим родителя `PropertyError` для более удобного наследования от него:
```js ```js
function PropertyError(property) { function PropertyError(property) {
this.name = "PropertyError"; this.name = "PropertyError";
this.property = property; this.property = property;
this.message = "Отсутствует свойство " + property; this.message = "Ошибка в свойстве " + property;
if (Error.captureStackTrace) { if (Error.captureStackTrace) {
Error.captureStackTrace(this, *!*this.constructor*/!*); // (*) Error.captureStackTrace(this, *!*this.constructor*/!*); // (*)
@ -195,9 +194,9 @@ PropertyError.prototype.constructor = PropertyError;
*/!* */!*
``` ```
В строке `(*)` это свойство было использовано, чтобы получить конструктор уже не в виде жёсткой ссылки `PropertyError`, а тот, который использован для текущего объекта. В наследнике там будет `PropertyRequiredError`, как и задумано. В строке `(*)` вместо ссылки на `PropertyError` используем `constructor` чтобы получить именно конструктор для текущего объекта. В наследнике там будет `PropertyRequiredError`, как и задумано.
Мы убрали одно упоминание, но с `this.name`, увы, сложности. Сейчас при наследовании оно будет всегда `"PropertyError"`. Все браузеры, кроме IE11-, поддерживают имя у Function Declaration, то есть имя функции можно было бы получить из `this.constructor.name`, но в IE11- это работать не будет. Мы убрали одну жёсткую привязку к `PropertyError`, но со второй (`this.name`), увы, сложности. Оно должно содержать имя ошибки, то есть, имя её функции-конструктора. Его можно получить через `this.name = this.constructor.name`, но в IE11- это работать не будет.
Если подерживать IE11-, то тут уж придётся в наследнике его записывать вручную. Если подерживать IE11-, то тут уж придётся в наследнике его записывать вручную.
@ -218,13 +217,68 @@ var err = new PropertyRequiredError("age");
alert( err instanceof PropertyError ); // true alert( err instanceof PropertyError ); // true
``` ```
Здесь заодно и `message` было перезаписано на более точное. Если хочется избежать записи и перезаписи, то можно оформить его в виде геттера через `Object.defineProperty`. Здесь заодно и `message` в наследнике было перезаписано на более точное. Если хочется избежать записи и перезаписи, то можно оформить его в виде геттера через `Object.defineProperty`.
## Итого ## Итого
<ul> <ul>
<li>Чтобы наследовать встроенному классу ошибок `Error`, нужно самостоятельно позаботиться о `name`, `message` и `stack`.</li> <li>Чтобы наследовать от ошибок `Error`, нужно самостоятельно позаботиться о `name`, `message` и `stack`.</li>
<li>Благодаря `instanceof` мы получили удобную поддержку иерархии ошибок, с возможностью в любой момент добавить новые классы, понятным кодом и предсказуемым поведением.</li> <li>Благодаря тому, что `instanceof` поддерживает наследование, удобно организуются проверки на нужный тип. В иерархию ошибок можно в любой момент добавить новые классы, с понятным кодом и предсказуемым поведением.</li>
</ul> </ul>
Чтобы создавать наследники от `Error` было проще, можно создать класс `CustomError`, записать в него универсальный код, наподобие `PropertyError` и далее наследовать уже от него. Чтобы создавать наследники от `Error` было проще, можно создать класс `CustomError`, записать в него универсальный код, наподобие `PropertyError` и далее наследовать уже от него:
```js
*!*
// общего вида "наша" ошибка
*/!*
function CustomError(message) {
this.name = "CustomError";
this.message = message;
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor);
} else {
this.stack = (new Error()).stack;
}
}
CustomError.prototype = Object.create(Error.prototype);
CustomError.prototype.constructor = CustomError;
*!*
// наследник
*/!*
function PropertyError(property) {
CustomError.call(this, "Отсутствует свойство " + property)
this.name = "PropertyError";
this.property = property;
}
PropertyError.prototype = Object.create(CustomError.prototype);
PropertyError.prototype.constructor = PropertyError;
*!*
// и ещё уровень
*/!*
function PropertyRequiredError(property) {
PropertyError.call(this, property);
this.name = 'PropertyRequiredError';
this.message = 'Отсутствует свойство ' + property;
}
PropertyRequiredError.prototype = Object.create(PropertyError.prototype);
PropertyRequiredError.prototype.constructor = PropertyRequiredError;
*!*
// использование
*/!*
var err = new PropertyRequiredError("age");
// пройдёт проверку
alert( err instanceof PropertyRequiredError ); // true
alert( err instanceof PropertyError ); // true
alert( err isntanceof CustomError ); // true
alert( err isntanceof Error ); // true
```

View file

@ -0,0 +1,174 @@
# Примеси
В JavaScript невозможно унаследовать от двух и более объектов. Ссылка `__proto__` -- только одна.
Но потребность такая существует -- к примеру, мы написали код, релизующий методы работы с шаблонизатором или методы по обмену событиями, и хочется легко и непринуждённо добавлять эти возможности к любому классу.
Обычно это делают через примеси.
Примесь (англ. mixin) -- класс или объект, реализующий какое-либо чётко выделенное поведение. Используется для уточнения поведения других классов, не предназначен для самостоятельного использования.
<!--break-->
## Пример примеси
Самый простой вариант примеси -- это объект с полезными методами, которые мы просто копируем в нужный прототип.
Например:
```js
//+ run
*!*
// примесь
*/!*
var sayHiMixin = {
sayHi: function() {
alert("Привет " + this.name);
},
sayBye: function() {
alert("Пока " + this.name);
}
};
*!*
// использование:
*/!*
function User(name) {
this.name = name;
}
// передать методы примеси
for(var key in sayHiMixin) User.prototype[key] = sayHiMixin[key];
// User "умеет" sayHi
new User("Вася").sayHi(); // Привет Вася
```
Как видно из примера, методы примеси активно используют `this` и предназначены именно для запуска в контексте "объекта-носителя примеси".
Если какие-то из методов примеси не нужны -- их можно перезаписать своими после копирования.
## Примесь для событий
Теперь пример из реальной жизни.
Важный аспект, который может понадобиться объектам -- это умение работать с событиями.
То есть, чтобы объект мог специальным вызовом генерировать "уведомление о событии", а на эти уведомления другие объекты могли "подписываться", чтобы их получать.
Например, объект "Пользователь" при входе на сайт может генерировать событие `"login"`, а другие объекты, например "Календарь" может такие уведомления получать и подгружать информацию о пользователе.
Или объект "Меню" может при выборе пункта меню генерировать событие `"select"` с информацией о выбранном пункте меню, а другие объекты -- подписавшись на это событие, будут узнавать об этом.
События -- это средство "поделиться информацией" с неопределённым кругом заинтересованных лиц. А там уже кому надо -- тот среагирует.
Примесь `eventMixin`, реализующая события:
```js
var eventMixin = {
/**
* Подписка на событие
* Использование:
* menu.on('select', function(item) { ... }
*/
on: function(eventName, handler) {
if (!this._eventHandlers) this._eventHandlers = {};
if (!this._eventHandlers[eventName]) {
this._eventHandlers[eventName] = [];
}
this._eventHandlers[eventName].push(handler);
},
/**
* Прекращение подписки
* menu.off('select', handler)
*/
off: function(eventName, handler) {
var handlers = this._eventHandlers && this._eventHandlers[eventName];
if (!handlers) return;
for(var i=0; i<handlers.length; i++) {
if (handlers[i] == handler) {
handlers.splice(i--, 1);
}
}
},
/**
* Генерация события с передачей данных
* this.trigger('select', item);
*/
trigger: function(eventName /*, ... */) {
if (!this._eventHandlers || !this._eventHandlers[eventName]) {
return; // обработчиков для события нет
}
// вызвать обработчики
var handlers = this._eventHandlers[eventName];
for (var i = 0; i < handlers.length; i++) {
handlers[i].apply(this, [].slice.call(arguments, 1));
}
}
};
```
Здесь есть три метода:
<ol>
<li>`.on(имя события, функция)` -- назначает функцию к выполнению при наступлении события с данным именем. Такие функции хранятся в защищённом свойстве объекта `_eventHandlers`.</li>
<li>`.off(имя события, функция)` -- удаляет функцию из списка предназначенных к выполнению.</li>
<li>`.trigger(имя события, аргументы)` -- генерирует событие, при этом вызываются все назначенные на него функции, и им передаются аргументы.</li>
</ol>
Использование:
```js
//+ run
// Класс Menu с примесью eventMixin
function Menu() {
// ...
}
for(var key in eventMixin) Menu.prototype[key] = eventMixin[key];
// Генерирует событие select при выборе значения
Menu.prototype.choose = function(value) {
*!*
this.trigger("select", value);
*/!*
}
// Создадим меню
var menu = new Menu();
// При наступлении события select вызвать эту функцию
*!*
menu.on("select", function(value) {
alert("Выбрано значение " + value);
});
*/!*
// Запускаем выбор (сработает событие)
menu.choose("123");
```
...То есть, смысл событий -- обычно в том, что объект, в процессе своей деятельности, внутри себя (`this.trigger`) генерирует уведомления, на которые внешний код через `menu.on(...)` может быть подписан. И узнавать из них ценную информцию о происходящем, например -- что выбран некий пункт меню.
Один раз написав методы `on/off/trigger` в примеси, мы затем можем использовать их во множестве прототипов.
## Итого
<ul>
<li>Примесь -- объект, содержащий методы и свойства для реализации конкретного функционала.
Возможны вариации этого приёма проектирования. Например, примесь может предусматривать конструктор, который должен запускаться в конструкторе объекта. Но как правило просто набора методов хватает.</li>
<li>Для добавления примеси в класс -- её просто "подмешивают" в прототип.</li>
<li>"Подмешать" можно сколько угодно примесей, но если имена методов в разных примесях совпадают, то возможны конфликты. Их уже разрешать -- разработчику. Например, можно заменить конфликтующий метод на свой, который будет решать несколько задач сразу. Конфликты при грамотно оформленных примесях возникают редко.</li></ul>

View file

@ -0,0 +1,21 @@
Двузначное шестнадцатиричное число -- это ``pattern`[0-9a-f]{2}` (с учётом флага ``pattern`/i`).
Нам нужно одно такое число, и за ним ещё 5 с двоеточиями перед ними: ``pattern`[0-9a-f]{2}(:[0-9a-f]{2}){5}`
И, наконец, совпадение должно начинаться в начале строки и заканчиваться -- в конце. То есть, строка целиком должна подходить под шаблон. Для этого обернём шаблон в ``pattern`^...$`.
Итого, в действии:
```js
//+ run
var re = /^[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}$/i;
alert( re.test('01:32:54:67:89:AB') ); // true
alert( re.test('0132546789AB') ); // false (нет двоеточий)
alert( re.test('01:32:54:67:89') ); // false (5 чисел, а не 6)
alert( re.test('01:32:54:67:89:ZZ') ) // false (ZZ в конце)
```

View file

@ -0,0 +1,20 @@
# Проверьте MAC-адрес
MAC-адрес сетевого интерфейса состоит из шести двузначиных шестандцатиричных чисел, разделённых двоеточием.
Например: ``subject`'01:32:54:67:89:AB'`.
Напишите регулярное выражение, которое по строке проверяет, является ли она корректным MAC-адресом.
Использование:
```js
var re = ваш регэксп
alert( re.test('01:32:54:67:89:AB') ); // true
alert( re.test('0132546789AB') ); // false (нет двоеточий)
alert( re.test('01:32:54:67:89') ); // false (5 чисел, а не 6)
alert( re.test('01:32:54:67:89:ZZ') ) // false (ZZ в конце)
```

View file

@ -0,0 +1,4 @@
# Предпросмотр (неготово)
Требуется добавить главу про предпросмотр lookahead.

View file

@ -1,4 +1,4 @@
# Найдите время # Найдите время в одном из форматов
Время может быть в формате `часы:минуты` или `часы-минуты`. И часы и минуты состоят из двух цифр, например `09:00`, `21-30`. Время может быть в формате `часы:минуты` или `часы-минуты`. И часы и минуты состоят из двух цифр, например `09:00`, `21-30`.

View file

@ -0,0 +1,18 @@
Начало шаблона очевидно: ``pattern`<style`.
А вот дальше... Мы не можем написать просто ``pattern`<style.*?>`, так как ``match`<styler>` удовлетворяет этому регэкспу.
Нужно уточнить его. После ``match`<style` должен быть либо пробел, после которого может быть что-то ещё, либо закрытие тега.
На языке регэкспов: ``pattern`<style(>|\s.*?>)`.
В действии:
```js
//+ run
var re = /<style(>|\s.*?>)/g;
alert( "<style> <styler> <style test>".match(re) ); // <style>, <style test>
```

View file

@ -0,0 +1,14 @@
# Найдите тег style
Напишите регулярное выражение, которое будет искать в тексте тег `<style>`. Подходят как обычный тег `<style>`, так и вариант с атрибутами `<style type="...">`.
Но регулярное выражение не должно находить `<styler>`!
Использование:
```js
var re = ваш регэксп
alert( "<style> <styler> <style test>".match(re) ); // <style>, <style test>
```

View file

@ -1,3 +1,3 @@
# Регулярные выражения [незавершён] # Регулярные выражения
Регулярные выражения -- мощный способ поиска и замены для строк. Регулярные выражения -- мощный способ поиска и замены для строк.

View file

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Before After
Before After

Some files were not shown because too many files have changed in this diff Show more