Структури в JavaScript


112

Раніше, коли мені потрібно було зберігати ряд пов’язаних змінних, я створив би клас.

function Item(id, speaker, country) {
    this.id = id;
    this.speaker = spkr;
    this.country = country;
}
var myItems = [
    new Item(1, 'john', 'au'),
    new Item(2, 'mary', 'us')
];

Але мені цікаво, чи це хороша практика. Чи є інші, кращі способи моделювання структури в Javascript?

Відповіді:


184

Єдина відмінність об'єктних літералів від побудованих об'єктів - це властивості, успадковані від прототипу.

var o = {
  'a': 3, 'b': 4,
  'doStuff': function() {
    alert(this.a + this.b);
  }
};
o.doStuff(); // displays: 7

Ви можете зробити фабрику структур.

function makeStruct(names) {
  var names = names.split(' ');
  var count = names.length;
  function constructor() {
    for (var i = 0; i < count; i++) {
      this[names[i]] = arguments[i];
    }
  }
  return constructor;
}

var Item = makeStruct("id speaker country");
var row = new Item(1, 'john', 'au');
alert(row.speaker); // displays: john

27
... і тоді я зрозумів фабричні функції. +1 за чіткою, зрозумілою та стислою відповіддю разом із заводським прикладом.
Джон

Мені подобається такий підхід, однак будьте обережні, якщо ви використовуєте компілятор закриття. У цьому випадку кортеж може бути доступний лише як рядок, оскільки властивості перейменовані. (Принаймні в розширеному режимі)
кап

Мені було цікаво про ефективність цього методу над об'єктними літералами.
c0degeas

Можливо, можливо також використовувати клас JS.
SphynxTech

31

Я завжди використовую об'єктні літерали

{id: 1, speaker:"john", country: "au"}

2
Хіба це не ускладнить підтримку (якщо ви хочете додати нове поле в майбутньому), а також набагато більше коду (перебираючи "id", "динамік", "країна" кожен раз)?
nickf

5
Це рівноцінно ретельно, як рішення з класами, оскільки JavaScript не переймається кількістю аргументів, з якими ви викликаєте функцію. Перебирання не є проблемою, якщо ви користуєтеся правильними інструментами, такими як Emacs. І ви можете бачити, що дорівнює тому, що робить помилки, як заміни аргументів застарілими.
vava

4
Але найбільший професіонал полягає в тому, що ви б написали менше коду, і він буде чистішим :)
vava

1
Перевірка @vava все ще залишається проблемою, оскільки є більше стрибків, ніж копіювання нового архетипу ___ (,,,). Також немає читабельності. Після того, як ви звикте до кодування, він new READABLE_PART(ignore everything in here)стає дуже доступним для сканування та самодокументування, на відміну від {read: "ignore", everything: "ignore", in: "ignore", here: "ignore"} // and then come up with READABLE_PARTвашої голови. Перша версія доступна для читання під час швидкого завантаження сторінки. По-друге, ви перетворюєте структуру, щоб просто зрозуміти.
Крістофер

19

Справжня проблема полягає в тому, що структури мови повинні бути типовими значеннями, а не типовими типами. Запропоновані відповіді пропонують використовувати об'єкти (які є еталонними типами) замість структур. Хоча це може слугувати своєму призначенню, це уникає того, що програміст дійсно бажає переваг використання типів значень (як примітивні) замість еталонного типу. Типи значень для одного не повинні спричиняти витоку пам'яті.


8

Я думаю, що найкращим способом є створення класу для імітації C-подібних структур .

Це прекрасний спосіб групувати пов’язані дані та спрощує передачу параметрів до функцій. Я також заперечую, що клас JavaScript більше нагадує структуру C ++, ніж клас C ++, враховуючи додаткові зусилля, необхідні для імітації реальних об'єктно-орієнтованих функцій.

Я виявив, що спроба зробити JavaScript схожішою на іншу мову швидко ускладнюється, але я повністю підтримую використання JavaScript-класів як функціональних структур.


Я хотів би мати щось на зразок структури, кортежі - те, що дозволяє сильно набирати набори даних - що обробляється під час компіляції і не має накладних хешмапів, як об’єкти
derekdreery

4

Після Markus в відповідь , в новіших версіях JS (ES6 я думаю) ви можете створити «» структури заводу більш просто з допомогою стрілок Функції і Rest параметр наступним чином:

const Struct = (...keys) => ((...v) => keys.reduce((o, k, i) => {o[k] = v[i]; return o} , {}))
const Item = Struct('id', 'speaker', 'country')
var myItems = [
    Item(1, 'john', 'au'),
    Item(2, 'mary', 'us')
];

console.log(myItems);
console.log(myItems[0].id);
console.log(myItems[0].speaker);
console.log(myItems[0].country);

Результатом цього є:

[ { id: 1, speaker: 'john', country: 'au' },
  { id: 2, speaker: 'mary', country: 'us' } ]
1
john
au

Ви можете зробити його схожим на найменування Python у nametuple:

const NamedStruct = (name, ...keys) => ((...v) => keys.reduce((o, k, i) => {o[k] = v[i]; return o} , {_name: name}))
const Item = NamedStruct('Item', 'id', 'speaker', 'country')
var myItems = [
    Item(1, 'john', 'au'),
    Item(2, 'mary', 'us')
];

console.log(myItems);
console.log(myItems[0].id);
console.log(myItems[0].speaker);
console.log(myItems[0].country);

І результати:

[ { _name: 'Item', id: 1, speaker: 'john', country: 'au' },
  { _name: 'Item', id: 2, speaker: 'mary', country: 'us' } ]
1
john
au

Це схоже на структуру на C / C ++ та інших мовах, але насправді це не так - властивості в об'єктах не гарантуються як упорядковані так, як описано: Визначення об'єкта з ECMAScript Третього видання (pdf): 4.3.3 Об'єкт Об'єкт є членом типу Object. Це невпорядкована колекція властивостей, кожна з яких містить примітивне значення, об'єкт чи функцію. Функція, що зберігається у властивості об'єкта, називається методом
tibetty

3

Я використовую об'єкти JSON-стилі для німих структур (відсутні функції членів).


1

Я створив невелику бібліотеку для визначення структури, якщо ви працюєте із сумісністю ES6.

Це JKT-аналізатор, який ви можете оглянути в сховищі проектів тут JKT Parser

Для прикладу ви можете створити структуру так

const Person = jkt`
    name: String
    age: Number
`

const someVar = Person({ name: "Aditya", age: "26" })

someVar.name // print "Aditya"
someVar.age // print 26 (integer)

someVar.toJSON() // produce json object with defined schema 

0

Це більше роботи над налаштуванням, але якщо ремонтоздатність обіймає разові зусилля, то це може бути у вашому випадку.

/**
 * @class
 */
class Reference {

    /**
     * @constructs Reference
     * @param {Object} p The properties.
     * @param {String} p.class The class name.
     * @param {String} p.field The field name.
     */
    constructor(p={}) {
        this.class = p.class;
        this.field = p.field;
    }
}

Переваги:

  • не пов'язаний з наказом аргументів
  • легко розширюється
  • тип підтримки сценарію:

введіть тут опис зображення

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.