Книга: JavaScript. Подробное руководство, 6-е издание

9.6.2. Пример: типы-перечисления

9.6.2. Пример: типы-перечисления

Перечислениями называются типы, которые могут принимать конечное количество значений, объявляемых (или «перечисляемых») при определении типа.

В языке С и его производных типы-перечисления объявляются с помощью ключевого слова enum. В ECMAScript 5 enum - это зарезервированное (но не используемое) слово, оставленное на тот случай, если когда-нибудь в JavaScript будут реализованы встроенные типы-перечисления. А пока в примере 9.7 демонстрируется, как можно определить собственный тип-перечисление на языке JavaScript. Обратите внимание, что здесь используется функция inherit() из примера 6.1.

Пример 9.7 содержит единственную функцию enumeration(). Однако она не является конструктором: она не определяет класс с именем «enumeration». Но она является фабричной функцией: при каждом вызове она создает и возвращает новый класс. Ниже показано, как ее можно использовать:

// Создать новый класс Coin с четырьмя возможными значениями:
// Coin.Penny, Coin.Nickel и т. д.
var Coin = enumeration({Penny: 1, Nickel:5, Dime:10, Quarter:25});
var c = Coin.Dime; // Это экземпляр нового класса
с instanceof Coin // => true: instanceof работает
c.constructor == Coin // => true: свойство constructor работает
Coin.Quarter + 3*Coin.Nickel // => 40: значения преобразуются в числа
Coin.Dime == 10 // => true: еще одно преобразование в число
Coin.Dime > Coin.Nickel // => true: операторы отношения работают
String(Coin.Dime) + ":" + Coin.Dime // => "Dime:10": преобразов, в строку

Цель этого примера состоит в том, чтобы продемонстрировать, что классы в языке JavaScript являются более гибкими и динамичными, чем статические классы в таких языках, как C++ и Java.

Пример 9.7 Типы-перечисления в JavaScript

// Эта функция создает новый тип-перечисление. Объект в аргументе определяет
// имена и значения каждого экземпляра класса. Возвращает функцию-конструктор,
// идентифицирующую новый класс. Отметьте, однако, что конструктор возбуждает
// исключение: его нельзя использовать для создания новых экземпляров типа.
// Возвращаемый конструктор имеет свойства, которые отображают имена в значения,
// а также массив значений values и функцию foreach() для выполнения итераций
function enumeration(namesToValues) {
  // Фиктивный конструктор, который будет использоваться как
  // возвращаемое значение.
  var enumeration = function() { throw "Нельзя создать экземпляр класса” +
                            Enumeration"; };
  // Перечислимые значения наследуют объект this,
  var proto = enumeration.prototype = {
    constructor: enumeration, // Идентификатор типа
    toString: function() { return this.name; }, // Возвращает имя
    valueOf: function() { return this.value; }, // Возвращает значение
    toJSON: function() { return this.name; } // Для сериализации
  };
  enumeration.values = []; // Массив перечислимых объектов-значений
  // Теперь создать экземпляры нового типа.
  for(name in namesToValues) { // Для каждого значения
    var е = inherit(proto); // Создать объект для его представления
    e.name = name; // Дать ему имя
    е.value = namesToValues[name]; // И значение
    enumeration[name] = е; // Сделать свойством конструктора
    enumeration.values.push(e); // И сохранить в массиве values
  }
  // Метод класса для обхода экземпляров класса в цикле
  enumeration.foreach = function(f,с) {
    for(var і = 0; і < this.values.length; i++) f.call(c,this.values[i]);
  };
  // Вернуть конструктор, идентифицирующий новый тип
  return enumeration;
}

Типичным начальным примером использования типов-перечислений может служить реализация перечисления для представления колоды игральных карт. Пример 9.8 использует функцию enumeration) именно для этого, а также определяет классы для представления карт и колод карт.[16]

Пример 9.8. Представление игральных карт в виде типов-перечислений

// Определение класса для представления игральной карты
function Card(suit, rank) {
  this.suit = suit; // Каждая карта имеет масть
  this.rank = rank; // и значение
}
// Следующие типы-перечисления определяют возможные масти и значения карт
  Card.Suit = enumeration({Clubs: 1, Diamonds: 2, Hearts:3, Spades:4});
  Card.Rank = enumeration({Two: 2, Three: 3, Four: 4, Five: 5, Six: 6,
                   Seven: 7, Eight: 8, Nine: 9, Ten: 10,
                   Jack: 11, Queen: 12, King: 13, Ace: 14});
// Определение текстового представления карты
Card.prototype.toString = function() {
  return this. rank.toString() + " " + this.suit.toString();
};
// Сравнивает значения двух карт в соответствии с правилами игры в покер
Card.prototype.compareTo = function(that){
  if (this.rank < that.rank) return -1;
  if (this.rank > that.rank) return 1;
  return 0;
};
// Функция упорядочения карт в соответствии с правилами игры в покер
Card.orderByRank = function(a,b) { return a.compareTo(b); };
// Функция упорядочения карт в соответствии с правилами игры в бридж
Card.orderBySuit = function(a,b) {
  if (a.suit < b.suit) return -1;
  if (a.suit > b.suit) return 1;
  if (a.rank < b.rank) return -1;
  if (a.rank > b.rank) return 1;
  return 0;
}:
// Определение класса представления стандартной колоды карт
function Deck() {
  var cards = this.cards = []; // Колода - просто массив карт
  Card.Suit.foreach(function(s) { // Инициализировать массив
    Card.Rank.foreach(function(r) {
      cards.push(new Card(s,r));
    })
  });
}
// Метод перемешивания: тасует колоду карт и возвращает ее
Deck.prototype.shuffle = function() {
  // Для каждого элемента массива: поменять местами
  // со случайно выбранным элементом ниже
  var deck = this.cards, len = deck.length;
  for(var і = len-1; і > 0; і--) {
    var r = Math.floor(Math.random()*(i+1)), temp; // Случайное число
    temp = deck[i], deck[i] = deck[r], deck[r] = temp; // Поменять
  }
  return this;
}
// Метод раздачи: возвращает массив карт
Deck.prototype.deal = function(n) {
  if (this.cards.length < n) throw "Карт для выдачи не хватает";
    return this.cards.splice(this.cards.length-n, n);
// Создает новую колоду карт, тасует ее и раздает как в игре в бридж
var deck = (new Deck()).shuffle();
var hand = deck.deal(13).sort(Card.orderBySuit);

Оглавление книги


Генерация: 1.724. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз