EDIT : Усі наведені нижче приклади в цій відповіді були відредаговані, щоб включити нову змінну шляху, отриману від ітератора відповідно до запиту @ supersan . Змінна шлях - це масив рядків, де кожна рядок у масиві являє собою кожний ключ, до якого можна було отримати доступ до отриманого ітераційного значення з вихідного об'єкта-джерела. Змінна контура може бути подана у функцію / метод getash getash . Або ви можете написати свою версію getash's get, яка обробляє лише масиви на зразок:
function get (object, path) {
return path.reduce((obj, pathItem) => obj ? obj[pathItem] : undefined, object);
}
const example = {a: [1,2,3], b: 4, c: { d: ["foo"] }};
// these paths exist on the object
console.log(get(example, ["a", "0"]));
console.log(get(example, ["c", "d", "0"]));
console.log(get(example, ["b"]));
// these paths do not exist on the object
console.log(get(example, ["e", "f", "g"]));
console.log(get(example, ["b", "f", "g"]));
EDIT : Ця відредагована відповідь вирішує нескінченне циклічне пересування.
Зупинка Пескіських нескінченних об'їзних обходів
Цей відредагований відповідь все ще надає одну з додаткових переваг моєї оригінальної відповіді, яка дозволяє використовувати надану функцію генератора , щоб використовувати більш чистий та простий ітерабельний інтерфейс (подумайте, використовуючи for ofпетлі, як у тому, for(var a of b)де bє ітерабельний і aє елементом ітерабельного ). Використовуючи функцію генератора, а також простіший api, він також допомагає при повторному використанні коду, роблячи його, так що вам не доведеться повторювати логіку ітерації скрізь, де ви хочете глибоко повторити властивості об'єкта, і це також дозволяє breakвийти з цикл, якщо ви хочете зупинити ітерацію раніше.
Одне, що я зауважую, що не було адресовано, і це не в моїй оригінальній відповіді, це те, що ви повинні бути обережними, обходячи довільні (тобто будь-який "випадковий" набір) об'єктів, тому що об'єкти JavaScript можуть бути самовідсилами. Це створює можливість мати нескінченне циклічне проходження. Немодифіковані дані JSON, однак, не можуть бути самостійними посиланнями, тому якщо ви використовуєте саме цей підмножина JS-об’єктів, вам не доведеться турбуватися про нескінченне циклічне проходження, і ви можете звернутися до моєї оригінальної відповіді чи інших відповідей. Ось приклад нескінченного обходу (зверніть увагу, що це не запущений фрагмент коду, оскільки в іншому випадку це може зламати вкладку браузера).
Також в об'єкті генератора в моєму відредагованому прикладі я вирішив використовувати Object.keysзамість цього for inітерацію лише об'єктів, які не є прототипами. Ви можете самостійно поміняти це, якщо вам потрібні ключі прототипу. Дивіться нижню частину моєї оригінальної відповіді щодо обох реалізацій із Object.keysта for in.
Гірше - це нескінченна петля на самореференційні об'єкти:
//your object
var o = {
foo:"bar",
arr:[1,2,3],
subo: {
foo2:"bar2"
}
};
// this self-referential property assignment is the only edited line
// from the below original example which makes the traversal
// non-terminating (i.e. it makes it infinite loop)
o.o = o;
function* traverse(o, path=[]) {
for (var i of Object.keys(o)) {
const itemPath = path.concat(i);
yield [i,o[i],itemPath];
if (o[i] !== null && typeof(o[i])=="object") {
//going one step down in the object tree!!
yield* traverse(o[I], itemPath);
}
}
}
//that's all... no magic, no bloated framework
for(var [key, value, path] of traverse(o)) {
// do something here with each key and value
console.log(key, value, path);
}
Щоб врятувати себе від цього, ви можете додати набір в межах закриття, так що коли функція вперше викликається, вона починає формувати пам’ять про бачені об'єкти і не продовжує ітерацію, коли вона стикається з уже баченим об'єктом. Наведений нижче фрагмент коду це робить, і, таким чином, обробляє нескінченні петлі.
Краще - це не буде нескінченним циклом на самореференційних об'єктах:
//your object
var o = {
foo:"bar",
arr:[1,2,3],
subo: {
foo2:"bar2"
}
};
// this self-referential property assignment is the only edited line
// from the below original example which makes more naive traversals
// non-terminating (i.e. it makes it infinite loop)
o.o = o;
function* traverse(o) {
const memory = new Set();
function * innerTraversal (o, path=[]) {
if(memory.has(o)) {
// we've seen this object before don't iterate it
return;
}
// add the new object to our memory.
memory.add(o);
for (var i of Object.keys(o)) {
const itemPath = path.concat(i);
yield [i,o[i],itemPath];
if (o[i] !== null && typeof(o[i])=="object") {
//going one step down in the object tree!!
yield* innerTraversal(o[i], itemPath);
}
}
}
yield* innerTraversal(o);
}
console.log(o);
//that's all... no magic, no bloated framework
for(var [key, value, path] of traverse(o)) {
// do something here with each key and value
console.log(key, value, path);
}
Оригінальний відповідь
Для більш нового способу зробити це, якщо ви не заперечуєте, щоб скинути IE і підтримувати в основному більш сучасні браузери (перевірте сумісність таблиці kengax в сумі сумісність). Для цього можна використовувати генератори es2015 . Я відповідно оновив відповідь @ TheHippo. Звичайно, якщо вам дуже потрібна підтримка IE, ви можете скористатись транслятором JavaScript babel .
//your object
var o = {
foo:"bar",
arr:[1,2,3],
subo: {
foo2:"bar2"
}
};
function* traverse(o, path=[]) {
for (var i in o) {
const itemPath = path.concat(i);
yield [i,o[i],itemPath];
if (o[i] !== null && typeof(o[i])=="object") {
//going one step down in the object tree!!
yield* traverse(o[i], itemPath);
}
}
}
//that's all... no magic, no bloated framework
for(var [key, value, path] of traverse(o)) {
// do something here with each key and value
console.log(key, value, path);
}
Якщо ви хочете тільки власні перелічуваних властивості ( в основному властивості без ланцюга прототипів) , ви можете змінити його ітерація , використовуючи Object.keysі в for...ofциклі замість:
//your object
var o = {
foo:"bar",
arr:[1,2,3],
subo: {
foo2:"bar2"
}
};
function* traverse(o,path=[]) {
for (var i of Object.keys(o)) {
const itemPath = path.concat(i);
yield [i,o[i],itemPath];
if (o[i] !== null && typeof(o[i])=="object") {
//going one step down in the object tree!!
yield* traverse(o[i],itemPath);
}
}
}
//that's all... no magic, no bloated framework
for(var [key, value, path] of traverse(o)) {
// do something here with each key and value
console.log(key, value, path);
}