Пример из Флэнагана
Всем привет
Продолжаю ковырять Флэнагана, в книге есть такой пример перечисления:
var Coin = enumeration({Penny: 1, Nickel:5, Dime:10, Quarter:25});
var dime = Coin.Dime;
В переменной dime должен создаваться новый объект, но при запуске скрипта, интерпретатор ругается на функцию enumeration. Подскажите, почему не работает код? |
Цитата:
|
Прошу прощения, мой косяк, чуть ниже было описание функции. Вот весь код:
function enumeration(namesToValues) {
// This is the dummy constructor function that will be the return value.
var enumeration = function() { throw "Can't Instantiate Enumerations"; };
// Enumerated values inherit from this object.
var proto = enumeration.prototype = {
constructor: enumeration, // Identify type
toString: function() { return this.name; }, // Return name
valueOf: function() { return this.value; }, // Return value
toJSON: function() { return this.name; } // For serialization
};
enumeration.values = []; // An array of the enumerated value objects
// Now create the instances of this new type.
for(name in namesToValues) { // For each value
var e = Object.create(proto); // Create an object to represent it
e.name = name; // Give it a name
e.value = namesToValues[name]; // And a value
enumeration[name] = e; // Make it a property of constructor
enumeration.values.push(e); // And store in the values array
}
// A class method for iterating the instances of the class
enumeration.foreach = function(f,c) {
for(var i = 0; i < this.values.length; i++) f.call(c,this.values[i]);
};
// Return the constructor that identifies the new type
return enumeration;
}
// Create a new Coin class with four values: Coin.Penny, Coin.Nickel, etc.
var Coin = enumeration({Penny: 1, Nickel:5, Dime:10, Quarter:25});
var c = Coin.Dime; // This is an instance of the new class
Скажите, как часто на практике вообще применяется подобное? |
И еще вопрос, в 24 строке определяется метод foreach, что нужно передавать в аргументы при его вызове?
Например я хочу вызвать его у объекта Coin. Что в (f, c) нужно? |
Цитата:
Цитата:
function enumeration(namesToValues) {
// This is the dummy constructor function that will be the return value.
var enumeration = function() { throw "Can't Instantiate Enumerations"; };
// Enumerated values inherit from this object.
var proto = enumeration.prototype = {
constructor: enumeration, // Identify type
toString: function() { return this.name; }, // Return name
valueOf: function() { return this.value; }, // Return value
toJSON: function() { return this.name; } // For serialization
};
enumeration.values = []; // An array of the enumerated value objects
// Now create the instances of this new type.
for(name in namesToValues) { // For each value
var e = Object.create(proto); // Create an object to represent it
e.name = name; // Give it a name
e.value = namesToValues[name]; // And a value
enumeration[name] = e; // Make it a property of constructor
enumeration.values.push(e); // And store in the values array
}
// A class method for iterating the instances of the class
enumeration.foreach = function(f,c) {
for(var i = 0; i < this.values.length; i++) f.call(c,this.values[i]);
};
// Return the constructor that identifies the new type
return enumeration;
}
// Create a new Coin class with four values: Coin.Penny, Coin.Nickel, etc.
var Coin = enumeration({Penny: 1, Nickel:5, Dime:10, Quarter:25});
var c = Coin.Dime; // This is an instance of the new class
var s = {sum : 0};
function fn(a)
{
this.sum += a.value
}
Coin.foreach(fn, s);
alert(s.sum)
|
Подскажите, как понять следующий пример с использованием enumeration:
function enumeration(namesToValues) {
// This is the dummy constructor function that will be the return value.
var enumeration = function() { throw "Can't Instantiate Enumerations"; };
// Enumerated values inherit from this object.
var proto = enumeration.prototype = {
constructor: enumeration, // Identify type
};
enumeration.values = []; // An array of the enumerated value objects
// Now create the instances of this new type.
for(name in namesToValues) { // For each value
var e = Object.create(proto); // Create an object to represent it
e.name = name; // Give it a name
e.value = namesToValues[name]; // And a value
enumeration[name] = e; // Make it a property of constructor
enumeration.values.push(e); // And store in the values array
}
// A class method for iterating the instances of the class
enumeration.foreach = function(f,c) {
for(var i = 0; i < this.values.length; i++) f.call(c,this.values[i]);
};
enumeration.print = function() {
console.log(this.values);
}
// Return the constructor that identifies the new type
return enumeration;
}
// Define a class to represent a playing card
function Card(suit, rank) {
this.suit = suit; // Each card has a suit
this.rank = rank; // and a rank
}
// These enumerated types define the suit and rank values
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});
// Define a class to represent a standard deck of cards
function Deck() {
var cards = this.cards = []; // A deck is just an array of cards
Card.Suit.foreach(function(s) { // Initialize the array
Card.Rank.foreach(function(r) {
cards.push(new Card(s, r));
});
});
}
var deck = new Deck();
Не понятно что происходит при создании var deck. Откуда берутся s и r в параметрах , если они не объявлены никак ранее? И как опять тут срабатывает enumeration.foreach = function(f,c), каким образом там оказывается список Card.Suit и Card.Rank, если он не передается в параметрах. Помогите пожалуйста разобраться, уже и так и сяк пробовал дебагером смотреть, ничего не понятно. |
|
Цитата:
Цитата:
|
Цитата:
|
Цитата:
значит s это f.call(c,this.values[i]); или currentValueТекущий обрабатываемый элемент в массиве. enumeration.foreach не нативный, но он делает тоже самое что forEach в Array. |
Цитата:
Цитата:
Пример 9.7 содержит единственную функцию enumeration(). Однако она не является конструктором: она не определяет класс с именем «enumeration». Но она является фабричной функцией: при каждом вызове она создает и возвращает новый класс. Ниже показано, как ее можно использовать:
var Coin = enumeration({Penny: 1, Nickel:5, Dime:10, Quarter:25});
var c = Coin.Dime; // This is an instance of the new class
c instanceof Coin // => true: instanceof works
c.constructor == Coin // => true: constructor property works
Coin.Quarter + 3*Coin.Nickel // => 40: values convert to numbers
Coin.Dime == 10 // => true: more conversion to numbers
Coin.Dime > Coin.Nickel // => true: relational operators work
String(Coin.Dime) + ":" + Coin.Dime // => "Dime:10": coerce to string
|
Цитата:
|
Цитата:
|
Вам на самом деле не нужна такая функция для перечисления, поскольку JavaScript предоставляет необходимые возможности для перечисления.
{
const coins = { Penny: 1, Nickel: 5, Dime: 10, Quarter: 25 };
const dime = coins.Dime;
let sum = 0;
for(const value of Object.values(coins)) {
sum += value;
}
alert(sum);
}
Или даже так...
{
const coins = { Penny: 1, Nickel: 5, Dime: 10, Quarter: 25 };
let sum = Object.values(coins).reduce((sum, value) => sum + value);
alert(sum);
}
Цитата:
Вот тот пример с игральными картами, но написанный с учётом возможностей JavaScript...
function Card(suit, rank) {
this.suit = suit;
this.rank = rank;
}
Card.Suits = { Clubs: 1, Diamonds: 2, Hearts: 3, Spades: 4 };
Card.Rank = {
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
};
function Deck() {
this.cards = [];
for(const suit of Object.values(Card.Suits))
for(const rank of Object.values(Card.Rank))
this.cards.push(new Card(suit, rank));
}
var deck = new Deck();
console.log(deck);
Можно сделать объект перечисляемым, добавив в него метод Symbol.iterator, в котором будет указано, каким образом перечислять, например я могу захотеть перечислять достоинства карт так, как они обозначаются на игральных картах...
var ranks = {
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,
* [Symbol.iterator]() {
for(const v in this) {
yield this[v] <= 10 ? this[v] : v[0];
}
}
};
// оператор расширения использует доступный итератор (он определён как Symbol.iterator)
console.log([...ranks]);
// Или можно использовать цикл for-of без Object.values,
// ranks стал перечислимым, поскольку содержит свойство-метод Symbol.iterator
for(const rank of ranks) {
console.log(rank);
}
Перечислимость объекта определяется наличием свойства-метода Symbol.iterator Если вам всё-таки нужен тип данных, чьё множество значении представляет собой ограниченный список идентификаторов, то подойдут символьные примитивы. Например, функцию enumeration (давайте я её назову Enum) можно определить так...
function Enum([definition]) {
return Object.freeze(
definition
.trim()
.split(/\s+/)
.reduce((m, p) => ({
...m,
[p]: Symbol(p),
__proto__: null
}), {})
);
}
// Примеры
const Color = Enum`
RED
GREEN
BLUE
`;
const Cardsuits = Enum`
CLUBS
DIAMONDS
HEARTS
SPADES
`;
console.log(Color, Cardsuits);
Хотя, такая функция в каком-то смысле лишняя, поскольку можно напрямую работать с символами...
const Color = Object.freeze({
RED: Symbol("RED"),
GREEN: Symbol("GREEN"),
BLUE: Symbol("BLUE")
});
console.log(Color);
|
Malleys,
:victory: :thanks: |
| Часовой пояс GMT +3, время: 18:05. |