Guten Tag, Freunde!
Dieser Hinweis hat keinen besonderen praktischen Wert. Auf der anderen Seite werden einige der "Grenz" -Funktionen von JavaScript untersucht, die Sie vielleicht interessant finden.
Im JavaScript Style Guide von Goggle wird empfohlen, nach Möglichkeit Prioritäten zu setzen.
Der Airbnb JavaScript Style Guide rät von der Verwendung von Iteratoren ab. Anstelle von For-In- und For-Of-Schleifen sollten Sie Funktionen höherer Ordnung wie map (), every (), filter (), find (), findIndex (), redu (), some () verwenden, um über Arrays und Object zu iterieren .keys (), Object.values (), Object.entries () zum Iterieren über Arrays von Objekten. Dazu später mehr.
Kehren wir zu Google zurück. Was bedeutet "wo möglich"?
Schauen wir uns einige Beispiele an.
Nehmen wir an, wir haben ein Array wie dieses:
const users = ["John", "Jane", "Bob", "Alice"];
Und wir wollen die Werte seiner Elemente an die Konsole ausgeben. Wie machen wir das?
//
log = (value) => console.log(value);
// for
for (let i = 0; i < users.length; i++) {
log(users[i]); // John Jane Bob Alice
}
// for-in
for (const item in users) {
log(users[item]);
}
// for-of
for (const item of users) {
log(item);
}
// forEach()
users.forEach((item) => log(item));
// map()
// -
// forEach()
users.map((item) => log(item));
Alles funktioniert super ohne zusätzlichen Aufwand von unserer Seite.
Angenommen, wir haben ein Objekt wie dieses:
const person = {
name: "John",
age: 30,
job: "developer",
};
Und wir wollen das Gleiche tun.
// for
for (let i = 0; i < Object.keys(person).length; i++) {
log(Object.values(person)[i]); // John 30 developer
}
// for-in
for (const i in person) {
log(person[i]);
}
// for-of & Object.values()
for (const i of Object.values(person)) {
log(i);
}
// Object.keys() & forEach()
Object.keys(person).forEach((i) => log(person[i]));
// Object.values() & forEach()
Object.values(person).forEach((i) => log(i));
// Object.entries() & forEach()
Object.entries(person).forEach((i) => log(i[1]));
Sieh den Unterschied? Wir müssen auf zusätzliche Tricks zurückgreifen, die darin bestehen, ein Objekt auf die eine oder andere Weise in ein Array umzuwandeln, weil:
for (const value of person) {
log(value); // TypeError: person is not iterable
}
Was sagt uns diese Ausnahme? Es heißt, dass das Objekt "Person" jedoch wie jedes andere Objekt keine iterierbare oder, wie sie sagen, iterative (iterierbare) Entität ist.
In diesem Abschnitt des Modern JavaScript Tutorial erfahren Sie, welche Iterables und Iteratoren sehr gut geschrieben sind . Mit Ihrer Erlaubnis werde ich nicht kopieren und einfügen. Ich empfehle jedoch dringend, 20 Minuten damit zu verbringen, es zu lesen. Andernfalls macht die weitere Präsentation für Sie wenig Sinn.
Nehmen wir an, wir mögen es nicht, dass Objekte nicht iterierbar sind, und wir möchten das ändern. Wie machen wir das?
Hier ist ein Beispiel von Ilya Kantor:
//
const range = {
from: 1,
to: 5,
};
// Symbol.iterator
range[Symbol.iterator] = function () {
return {
//
current: this.from,
//
last: this.to,
//
next() {
//
if (this.current <= this.last) {
// ,
return { done: false, value: this.current++ };
} else {
// ,
return { done: true };
}
},
};
};
for (const num of range) log(num); // 1 2 3 4 5
// !
Das bereitgestellte Beispiel ist im Wesentlichen ein Generator, der mit einem Iterator erstellt wurde. Aber zurück zu unserem Objekt. Eine Funktion zum Verwandeln eines regulären Objekts in ein iterierbares Objekt könnte folgendermaßen aussehen:
const makeIterator = (obj) => {
// "size", "length"
Object.defineProperty(obj, "size", {
value: Object.keys(obj).length,
});
obj[Symbol.iterator] = (
i = 0,
values = Object.values(obj)
) => ({
next: () => (
i < obj.size
? { done: false, value: values[i++] }
: { done: true }
),
});
};
Wir überprüfen:
makeIterator(person);
for (const value of person) {
log(value); // John 30 developer
}
Passiert! Jetzt können wir ein solches Objekt einfach in ein Array konvertieren und die Anzahl seiner Elemente über die Eigenschaft "size" ermitteln:
const arr = Array.from(person);
log(arr); // ["John", 30, "developer"]
log(arr.size); // 3
Wir können unseren Funktionscode vereinfachen, indem wir einen Generator anstelle eines Iterators verwenden:
const makeGenerator = (obj) => {
//
//
Object.defineProperty(obj, "isAdult", {
value: obj["age"] > 18,
});
obj[Symbol.iterator] = function* () {
for (const i in this) {
yield this[i];
}
};
};
makeGenerator(person);
for (const value of person) {
log(value); // John 30 developer
}
const arr = [...person];
log(arr); // ["John", 30, "developer"]
log(person.isAdult); // true
Können wir die "nächste" Methode unmittelbar nach dem Erstellen der Iterable verwenden?
log(person.next().value); // TypeError: person.next is not a function
Damit wir eine solche Gelegenheit haben, müssen wir zuerst den Symbol.iterator des Objekts aufrufen:
const iterablePerson = person[Symbol.iterator]();
log(iterablePerson.next()); // { value: "John", done: false }
log(iterablePerson.next().value); // 30
log(iterablePerson.next().value); // developer
log(iterablePerson.next().done); // true
Wenn Sie ein iterierbares Objekt erstellen müssen, ist es besser, Symbol.iterator sofort darin zu definieren. Am Beispiel unseres Objekts:
const person = {
name: "John",
age: 30,
job: "developer",
[Symbol.iterator]: function* () {
for (const i in this) {
yield this[i];
}
},
};
Weitermachen. Wo hin? In die Metaprogrammierung. Was ist, wenn wir die Werte von Objekteigenschaften wie in Arrays nach Index abrufen möchten? Und was ist, wenn bestimmte Eigenschaften eines Objekts unveränderlich sein sollen? Lassen Sie uns dieses Verhalten mithilfe eines Proxys implementieren . Warum einen Proxy verwenden? Nun, wenn auch nur, weil wir können:
const makeProxy = (obj, values = Object.values(obj)) =>
new Proxy(obj, {
get(target, key) {
//
key = parseInt(key, 10);
// , 0
if (key !== NaN && key >= 0 && key < target.size) {
//
return values[key];
} else {
// ,
throw new Error("no such property");
}
},
set(target, prop, value) {
// "name" "age"
if (prop === "name" || prop === "age") {
//
throw new Error(`this property can't be changed`);
} else {
//
target[prop] = value;
return true;
}
},
});
const proxyPerson = makeProxy(person);
//
log(proxyPerson[0]); // John
//
log(proxyPerson[2]); // Error: no such property
//
log((proxyPerson[2] = "coding")); // true
//
log((proxyPerson.name = "Bob")); // Error: this property can't be changed
Welche Schlussfolgerungen können wir daraus ziehen? Sie können natürlich selbst ein iterierbares Objekt erstellen (es ist JavaScript, Baby), aber die Frage ist, warum. Wir stimmen dem Airbnb-Leitfaden zu, dass es mehr als genug native Methoden gibt, um die gesamte Bandbreite der Aufgaben im Zusammenhang mit der Iteration über Schlüssel und Werte von Objekten zu lösen. Es ist nicht erforderlich, das Rad neu zu erfinden. Der Leitfaden von Google kann durch die Tatsache verdeutlicht werden, dass die for-of-Schleife für Arrays und Arrays von Objekten bevorzugt werden sollte. Für Objekte als solche können Sie die for-in-Schleife verwenden, aber besser - integrierte Funktionen.
Ich hoffe, Sie haben etwas Interessantes für sich gefunden. Vielen Dank für Ihre Aufmerksamkeit.