Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Пример из Флэнагана (https://javascript.ru/forum/misc/74460-primer-iz-flehnagana.html)

RuBrain 14.07.2018 11:12

Пример из Флэнагана
 
Всем привет

Продолжаю ковырять Флэнагана, в книге есть такой пример перечисления:

var Coin = enumeration({Penny: 1, Nickel:5, Dime:10, Quarter:25});
var dime = Coin.Dime;


В переменной dime должен создаваться новый объект, но при запуске скрипта, интерпретатор ругается на функцию enumeration. Подскажите, почему не работает код?

рони 14.07.2018 12:21

Цитата:

Сообщение от RuBrain
интерпретатор ругается на функцию enumeration

и где сама функция?

RuBrain 26.07.2018 13:08

Прошу прощения, мой косяк, чуть ниже было описание функции. Вот весь код:

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


Скажите, как часто на практике вообще применяется подобное?

RuBrain 26.07.2018 16:08

И еще вопрос, в 24 строке определяется метод foreach, что нужно передавать в аргументы при его вызове?

Например я хочу вызвать его у объекта Coin. Что в (f, c) нужно?

рони 26.07.2018 18:17

Цитата:

Сообщение от RuBrain
метод foreach, что нужно передавать в аргументы при его вызове?

Цитата:

Сообщение от Rise
function и context

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)

RuBrain 01.08.2018 22:57

Подскажите, как понять следующий пример с использованием 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, если он не передается в параметрах. Помогите пожалуйста разобраться, уже и так и сяк пробовал дебагером смотреть, ничего не понятно.

RuBrain 02.08.2018 05:50

https://c.radikal.ru/c03/1808/62/76c3d91add9b.png

рони 02.08.2018 08:47

Цитата:

Сообщение от RuBrain
Откуда берутся s и r в параметрах ,

Цитата:

currentValueТекущий обрабатываемый элемент в массиве.
/Array/forEach

RuBrain 02.08.2018 12:05

Цитата:

Сообщение от рони (Сообщение 491527)

ОК, если массив, то все понятно, но каким образом Card.Suit становится массивом? В дебагере если я останавливаюсь на нем, то это объект-функция с некоторыми значениями. В Card.Suit свой метод foreach, который принимает 2 аргумента f и c. С первым f вроде понятно, туда передается функция cards.push(new Card(suit, rank), а что с "c"?

рони 02.08.2018 13:51

Цитата:

Сообщение от RuBrain
но каким образом Card.Suit становится массивом?

никаким, но в Card.Suit есть массив и есть метод обработки каждого элемента массива enumeration.foreach
значит s это f.call(c,this.values[i]); или currentValueТекущий обрабатываемый элемент в массиве.

enumeration.foreach не нативный, но он делает тоже самое что forEach в Array.

RuBrain 03.08.2018 13:10

Цитата:

Сообщение от Rise (Сообщение 491553)
RuBrain,
Что-то у вас отличается, второй вариант не имеет смысла, оно итак так.

Я удалил не нужное, чтобы проще читался код.


Цитата:

Сообщение от Rise (Сообщение 491553)
И что-то сам автор начудил с этой функцией enumeration, как-то это он не по javascript-ному сделал, зачем-то всё засунул в свойства функции и запретил ее вызывать, наверное по опыту из какого-то другого языка, где всякие статические классы есть, он это сделал, но без лишних понтов смысл такой:

Ну он вот как объяснил:

Пример 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

RuBrain 03.08.2018 13:17

Цитата:

Сообщение от рони (Сообщение 491550)
никаким, но в Card.Suit есть массив и есть метод обработки каждого элемента массива enumeration.foreach
значит s это f.call(c,this.values[i]); или currentValueТекущий обрабатываемый элемент в массиве.

enumeration.foreach не нативный, но он делает тоже самое что forEach в Array.

Да, там есть массив Card.Suit.values, с ним вроде как все ясно. Не совсем понятна переменная c.

рони 03.08.2018 13:37

Цитата:

Сообщение от RuBrain
Не совсем понятна переменная c

нет c, нет контекста в данном случае.

Malleys 04.08.2018 03:32

Вам на самом деле не нужна такая функция для перечисления, поскольку 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);
}


Цитата:

Сообщение от RuBrain
Как часто на практике вообще применяется подобное?

На практике скорей применяются, например, цикл for-in, функция Object.values, функция(-генератор) как значение свойства Symbol.iterator у объекта, описывающая, как перечислять объект.

Вот тот пример с игральными картами, но написанный с учётом возможностей 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);

рони 04.08.2018 09:39

Malleys,
:victory: :thanks:


Часовой пояс GMT +3, время: 00:39.