Дэвид Флэнаган - JavaScript. Подробное руководство, 6-е издание
- Название:JavaScript. Подробное руководство, 6-е издание
- Автор:
- Жанр:
- Издательство:Символ-Плюс
- Год:2012
- Город:СПб
- ISBN:978-5-93286-215-5
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Дэвид Флэнаган - JavaScript. Подробное руководство, 6-е издание краткое содержание
Эта книга - одновременно и руководство программиста, и полноценный справочник по базовому языку JavaScript и клиентским прикладным интерфейсам, предоставляемым веб-броузерами.
JavaScript. Подробное руководство, 6-е издание - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
* возвращает это значение.
* -Если о имеет свойство constructor, а конструктор имеет имя, возвращает
* имя конструктора.
* -Иначе просто возвращает "Object".
**/
function type(o) {
var t, c, n; // type, class, name
// Специальный случай для значения null:
if (о === null) return "null":
// Другой специальный случай: NaN - единственное значение, не равное самому себе:
if (о !== о) return "nan";
// Применять typeof для любых значений, отличных от "object".
// Так идентифицируются простые значения и функции,
if ((t = typeof о) !== "object") return t;
// Вернуть класс объекта, если это не "Object".
// Так идентифицируется большинство встроенных объектов,
if ((с = classof(o)) !== "Object") return с;
// Вернуть имя конструктора объекта, если имеется
if (о.constructor && typeof о.constructor === "function" &&
(n = о.constructor.getName())) return n;
// He удалось определить конкретный тип, поэтому остается лишь
// просто вернуть "Object"
return "Object";
}
// Возвращает класс объекта,
function classof(o) {
return Object.prototype.toString.call(о).slice(8,-1);
};
// Возвращает имя функции (может быть "") или null - для объектов,
// не являющихся функциями
Function.prototype.getName = function() {
if ("name" in this) return this.name;
return this.name = this.toString().match(/function\s*([^(]*)\(/)[1];
):
Этот прием, основанный на использовании имени конструктора для идентификации класса объекта, имеет ту же проблему, что и прием на основе использования свойства constructor
: не все объекты имеют свойство constructor
. Кроме того, не все функции имеют имена. Если определить конструктор, используя выражение определения неименованной функции, метод getName()
будет возвращать пустую строку:
// Этот конструктор не имеет имени
var Complex = function(x,у) { this.r = х; this.і = у; }
// Этот конструктор имеет имя
var Range = function Range(f.t) { this.from = f; this.to = t; }
9.5.4. Грубое определение типа
Ни один из приемов определения класса объекта, описанных выше, не свободен от проблем, по крайней мере, в клиентском JavaScript. Альтернативный подход состоит в том, чтобы вместо вопроса «какому классу принадлежит объект?» задать вопрос «что может делать этот объект?». Этот подход является типичным в таких языках программирования, как Python и Ruby, и носит название грубое определение типа (
Когда я вижу птицу, которая ходит, как утка, плавает, как утка и крякает, как утка, я называю ее уткой.
Для программистов на языке JavaScript этот афоризм можно интерпретировать так: «Если объект может ходить, плавать и крякать как объект класса Duck, его можно считать объектом класса Duck, даже если он не наследует объект-прототип класса Duck».
Примером может служить класс Range
из примера 9.2. Этот класс предназначен для представления диапазонов чисел. Однако обратите внимание, что конструктор Range()
не проверяет типы аргументов, чтобы убедиться, что они являются числами. Аналогично метод includes()
использует оператор <=
, но не делает никаких предположений о типах значений границ диапазона. Благодаря тому что класс не ограничивается определенным типом значений, его метод includes()
способен обрабатывать значения границ любых типов, которые могут сравниваться с помощью операторов отношения:
var lowercase = new Range("a", 'z');
var thisYear = new Range(new Date(2009, 0, 1), new Date(2010, 0, 1));
Метод foreach()
класса Range
также не проверяет типы значений границ, но он использует функцию Math.ceil()
и оператор ++
, вследствие чего может применяться только к числовым значениям границ диапазона.
В качестве еще одного примера вспомним объекты, подобных массивам, обсуждавшиеся в разделе 7.11. Во многих случаях нам не требуется знать, действительно ли объект является экземпляром класса Array
: вполне достаточно знать, что он имеет свойство length
с неотрицательным целочисленным значением. Если посчитать, что целочисленное свойство length
- это способ массивов «ходить», то мы могли бы сказать, что любой объект, который умеет «ходить» так же, можно (во многих случаях) отнести к массивам.
Однако имейте в виду, что свойство length
настоящих массивов обладает особым поведением: свойство length
автоматически обновляется при добавлении нового элемента, а когда значение свойства length
уменьшается, массив автоматически усекается. Можно было бы сказать, что эти две особенности описывают, как массивы «плавают» и «крякают». Если вы пишете программу, где требуется, чтобы объект «плавал» и «крякал» как массив, вы не сможете использовать в ней объект, который только «ходит» как массив.
Примеры грубого определения типа, представленные выше, опираются на возможность сравнения объектов с помощью оператора <
и на особенности поведения свойства length
. Однако чаще всего под грубым определением типа подразумевается проверка наличия в объекте одного или более методов. Строго типизированная функция triathlon()
могла бы потребовать, чтобы ее аргумент был объектом класса TriAthlete
. Альтернативная реализация, выполняющая грубую проверку типа, могла бы принимать любой объект, имеющий методы walk(), swim()
и bike().
Если говорить более конкретно, можно было бы переписать класс Range
так, чтобы вместо операторов <
и ++
он использовал бы методы compareTo()
и succ()
объектов значений границ.
Можно подойти к определению типа либерально: просто предположить, что входные объекты реализуют все необходимые методы, и не выполнять никаких проверок. Если предположение окажется ошибочным, при попытке вызвать несуществующий метод возникнет ошибка. Другой подход заключается в реализации проверки входных объектов. Однако, вместо того чтобы проверять их принадлежность к определенному классу, можно проверить наличие методов с определенными именами. Это позволит отвергнуть входные объекты раньше и вернуть более информативное сообщение об ошибке.
Читать дальшеИнтервал:
Закладка: