Selamat siang teman!
Catatan ini tidak memiliki nilai praktis tertentu. Di sisi lain, ini mengeksplorasi beberapa fitur JavaScript "garis batas" yang mungkin menarik bagi Anda.
Panduan Gaya JavaScript Goggle menyarankan Anda untuk memprioritaskan untuk-dari jika memungkinkan.
Panduan Gaya JavaScript Airbnb melarang penggunaan iterator. Alih-alih perulangan for-in dan for-of, Anda harus menggunakan fungsi tingkat tinggi seperti map (), every (), filter (), find (), findIndex (), reduce (), some () untuk melakukan iterasi pada array dan Object. .keys (), Object.values (), Object.entries () untuk melakukan iterasi pada array objek. Lebih lanjut tentang itu nanti.
Mari kembali ke Google. Apa artinya "jika mungkin"?
Mari kita lihat beberapa contoh.
Katakanlah kita memiliki array seperti ini:
const users = ["John", "Jane", "Bob", "Alice"];
Dan kami ingin menampilkan nilai elemennya ke konsol. Bagaimana kita melakukan ini?
//
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));
Semuanya bekerja dengan baik tanpa usaha ekstra dari kami.
Sekarang, misalkan kita memiliki objek seperti ini:
const person = {
name: "John",
age: 30,
job: "developer",
};
Dan kami ingin melakukan hal yang sama.
// 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]));
Lihat perbedaannya? Kami harus menggunakan trik tambahan, yang terdiri dari mengubah objek menjadi array dengan satu atau lain cara, karena:
for (const value of person) {
log(value); // TypeError: person is not iterable
}
Apa yang diceritakan oleh pengecualian ini kepada kita? Ia mengatakan bahwa objek "orang", bagaimanapun, seperti objek lainnya, bukanlah sebuah iterable atau, seperti yang mereka katakan, sebuah entitas yang dapat diulang (iterable).
Tentang iterable dan iterator yang ditulis dengan sangat baik di bagian Tutorial JavaScript Modern ini. Dengan izin Anda, saya tidak akan menyalin-tempel. Namun, saya sangat merekomendasikan menghabiskan 20 menit untuk membacanya. Jika tidak, presentasi selanjutnya tidak akan masuk akal bagi Anda.
Katakanlah kita tidak suka bahwa objek tidak dapat diulang, dan kita ingin mengubahnya. Bagaimana kita melakukan ini?
Berikut adalah contoh yang diberikan oleh 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
// !
Pada dasarnya, contoh yang diberikan adalah generator yang dibuat dengan iterator. Tapi kembali ke objek kita. Fungsi untuk mengubah objek biasa menjadi iterable mungkin terlihat seperti ini:
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 }
),
});
};
Kami memeriksa:
makeIterator(person);
for (const value of person) {
log(value); // John 30 developer
}
Terjadi! Sekarang kita dapat dengan mudah mengubah objek seperti itu menjadi array, serta mendapatkan jumlah elemennya melalui properti "size":
const arr = Array.from(person);
log(arr); // ["John", 30, "developer"]
log(arr.size); // 3
Kita dapat menyederhanakan kode fungsi kita dengan menggunakan generator sebagai pengganti iterator:
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
Bisakah kita menggunakan metode "selanjutnya" segera setelah membuat iterable?
log(person.next().value); // TypeError: person.next is not a function
Agar kita memiliki kesempatan ini, pertama-tama kita harus memanggil Symbol.iterator objek:
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
Perlu dicatat bahwa jika Anda perlu membuat objek iterable, lebih baik segera mendefinisikan Symbol.iterator di dalamnya. Menggunakan objek kami sebagai contoh:
const person = {
name: "John",
age: 30,
job: "developer",
[Symbol.iterator]: function* () {
for (const i in this) {
yield this[i];
}
},
};
Bergerak. Ke mana harus pergi? Ke dalam metaprogramming. Bagaimana jika kita ingin mendapatkan nilai properti objek berdasarkan indeks, seperti dalam array? Dan bagaimana jika kita ingin properti tertentu dari suatu objek tidak dapat diubah. Mari terapkan perilaku ini menggunakan proxy . Mengapa menggunakan proxy? Nah, kalau hanya karena kita bisa:
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
Kesimpulan apa yang bisa kita ambil dari semua ini? Anda dapat, tentu saja, membuat sendiri objek yang dapat diulang (ini JavaScript, sayang), tetapi pertanyaannya adalah mengapa. Kami setuju dengan Panduan Airbnb bahwa ada lebih dari cukup metode asli untuk menyelesaikan seluruh rentang tugas yang terkait dengan pengulangan kunci dan nilai objek, tidak perlu "menemukan kembali roda". Panduan dari Google dapat diklarifikasi oleh fakta bahwa for-of loop harus lebih disukai untuk array dan array objek, untuk objek seperti itu, Anda dapat menggunakan for-in loop, tetapi lebih baik - fungsi built-in.
Semoga Anda menemukan sesuatu yang menarik untuk diri Anda sendiri. Terima kasih atas perhatian Anda.