Struktury w Javascript


112

Wcześniej, gdy musiałem przechowywać kilka powiązanych zmiennych, tworzyłem klasę.

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')
];

Ale zastanawiam się, czy to dobra praktyka. Czy są jakieś inne, lepsze sposoby na symulację struktury w JavaScript?

Odpowiedzi:


184

Jedyną różnicą między literałami obiektów a skonstruowanymi obiektami są właściwości odziedziczone z prototypu.

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

Możesz zrobić fabrykę struktur.

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
... i wtedy zrozumiałem funkcje fabryczne. +1 za jasną, zrozumiałą i zwięzłą odpowiedź, wraz z przykładem fabryki.
John

Podoba mi się to podejście, jednak zachowaj ostrożność, jeśli używasz kompilatora zamknięcia. W tym przypadku do krotki można uzyskać dostęp tylko jako ciąg znaków, ponieważ nazwy właściwości są zmieniane. (Przynajmniej w trybie zaawansowanym)
kap

Byłem ciekawy skuteczności tej metody w porównaniu z literałami obiektu.
c0degeas

Możliwe jest również użycie klasy JS.
SphynxTech

31

Zawsze używam literałów obiektów

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

2
czy nie utrudniłoby to znacznie utrzymania (czy w przyszłości chciałbyś dodać nowe pole), a także znacznie więcej kodu (za każdym razem przepisywanie „id”, „speaker”, „country”)?
nickf

5
Jest dokładnie tak samo zarządzalny jak rozwiązanie z klasami, ponieważ JavaScript nie dba o liczbę argumentów, z którymi wywołujesz funkcję. Ponowne pisanie nie jest problemem, jeśli używasz odpowiednich narzędzi, takich jak Emacs. I możesz zobaczyć, co równa się temu, co powoduje, że błędy, takie jak wymiana argumentów, są przestarzałe.
vava

4
Ale największym plusem jest to, że napiszesz mniej kodu i będzie czystszy :)
vava

1
Przepisywanie @vava jest nadal problemem, ponieważ jest więcej skoków niż kopiowanie nowego archetypu ___ (,,,). Nie ma też czytelności. Kiedy już przyzwyczaisz się do kodowania, new READABLE_PART(ignore everything in here)staje się bardzo łatwe do zeskanowania i samodokumentowania, a nie {read: "ignore", everything: "ignore", in: "ignore", here: "ignore"} // and then come up with READABLE_PARTw głowie. Pierwsza wersja jest czytelna podczas szybkiego przewijania w górę. Po drugie, refaktoryzujesz na struktury tylko po to, by zrozumieć.
Christopher

19

Prawdziwym problemem jest to, że struktury w języku mają być typami wartości, a nie typami odwołań. Proponowane odpowiedzi sugerują użycie obiektów (będących typami referencyjnymi) w miejsce struktur. Chociaż może to służyć swojemu celowi, omija punkt, w którym programista rzeczywiście chciałby korzyści z używania typów wartości (takich jak prymityw) zamiast typu referencyjnego. Po pierwsze, typy wartości nie powinny powodować wycieków pamięci.


8

Myślę, że stworzenie klasy do symulacji struktur podobnych do C, tak jak robiłeś, jest najlepszym sposobem.

To świetny sposób na grupowanie powiązanych danych i upraszcza przekazywanie parametrów do funkcji. Twierdzę również, że klasa JavaScript jest bardziej podobna do struktury C ++ niż klasy C ++, biorąc pod uwagę dodatkowy wysiłek potrzebny do symulacji rzeczywistych funkcji obiektowych.

Zauważyłem, że próba upodobnienia JavaScript do innego języka szybko się komplikuje, ale w pełni popieram używanie klas JavaScript jako struktur bez funkcji.


Chciałbym mieć coś takiego elemencie, krotki - coś, co pozwala silnie wpisane zbiory danych - które są rozpatrywane w compiletime i nie ma napowietrznej hashmaps jak obiekty
derekdreery

4

Podążając za odpowiedzią Markusa , w nowszych wersjach JS (myślę, że ES6) możesz stworzyć fabrykę 'struct' w prostszy sposób, używając funkcji strzałek i parametru reszty, jak poniżej:

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);

Wynikiem tego jest:

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

Możesz sprawić, że będzie wyglądać podobnie do namedtuple w Pythonie:

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);

A wyniki:

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

Wygląda jak struktura w C / C ++ i innych językach, ale w rzeczywistości tak nie jest - właściwości w obiektach nie mają gwarancji uporządkowania opisanego w następujący sposób: Definicja obiektu z ECMAScript Third Edition (pdf): 4.3.3 Object Obiekt jest członkiem typu Object. Jest to nieuporządkowany zbiór właściwości, z których każda zawiera pierwotną wartość, obiekt lub funkcję. Funkcja przechowywana we właściwości obiektu nazywana jest metodą
tybetty


1

Zrobiłem małą bibliotekę, aby zdefiniować strukturę, jeśli pracujesz z kompatybilnością ES6.

Jest to parser JKT, możesz pobrać repozytorium projektu tutaj JKT Parser

Na przykład możesz stworzyć swoją strukturę w ten sposób

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

Konfiguracja wymaga więcej pracy, ale jeśli łatwość konserwacji przewyższa jednorazowy wysiłek, może tak być w Twoim przypadku.

/**
 * @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;
    }
}

Zalety:

  • nie jest związany z kolejnością argumentów
  • łatwo wysuwane
  • obsługa skryptów typu:

wprowadź opis obrazu tutaj

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.