Условно меняем порядок ключей в Javascript объектах - PullRequest
0 голосов
/ 17 марта 2020

Я хотел бы изменить порядок ключей в javascript объекте на основе логического условия. Порядок ключей используется для визуализации элементов dom в порядке. Создание нового объекта с переупорядоченными ключами будет работать. Однако меня беспокоит, что условия if / else увеличатся, если я хочу добавить новые заказы в будущем. Есть ли надежный / правильный способ контролировать заказ с помощью ES6 / Vanilla JS?

    var condition = false, obj = {};
    if(condition) {
        obj = {
           "a": 1,
           "b": 2,
           "c": 3
        };
    } else {
        obj = {
           "a": 1,
           "c": 3,
           "b": 2
        };
    }

1 Ответ

0 голосов
/ 18 марта 2020

Ответ:

Вы можете сделать это, создав Конструктор , который включает частные методы для сортировки и определяет итератор .

Это будет сводить ваши логики c к:

if ( condition ) {
  obj.sort( [ "a", "b", "c" ] );
} else {
  obj.sort( [ "a", "c", "b" ] );
}

Пример:

// Constructor
function ObjectSortable( obj ) {
  const private = {
    sort: function( order ) {
      this[ Symbol.iterator ] = function*() {
        order = order.map( prop => this[ prop ] );
        yield* order;
      };
      return this;
    }
  }
  
  let sortable = new Proxy( obj, {
    get: function( target, prop, receiver ) {
      if ( private[ prop ] ) {
        return private[ prop ].bind( sortable );
      }
      return target[ prop ];
    }
  } );
  
  sortable.sort( Object.keys( obj ) );
  return sortable;
}

// Use
var condition = false,
  obj = ObjectSortable( {
    "a": 1,
    "b": 2,
    "c": 3
  } );
  
if ( condition ) {
  obj.sort( [ "a", "b", "c" ] );
} else {
  obj.sort( [ "a", "c", "b" ] );
}

for(let item of obj) {
   console.log(item);
}

Шаги:

Вот шаги, которые мы выполнили для решения проблемы:

  • Создание Конструктор , который принимает Объект
  • Создание частных методов Объект в замыкании
    • Создание sort метод, который принимает sort order и определяет новый Symbol.iterator в context (наш переданный в Object )
  • создать прокси для нашего переданного объекта
    • создать обработчик get для прокси , Это будет обрабатываться при попытке поиска свойства в нашем Object .
      • В течение получить , если имя свойства соответствует приватному методу, bind нашему Объекту как context для нашего частного метода и вызов его.
    • вернуть наш Proxy

Если это звучит как много, не волнуйтесь! Я буду go по кусочкам.


Подробное описание конструктора:

Конструктор ObjectSortable принимает Object в качестве единственного параметра.

function ObjectSortable( obj ) {...}

Создает объект для частных методов и создает метод sort, который принимает Array из ключей в качестве единственного параметра ( order ):

  const private = {
   sort: function( order ) {
       ...

    }
  }

Sort назначает новый итератор контексту и использует order Array для получения значений. Затем он возвращает контекст.

    sort: function( order ) {
      this[ Symbol.iterator ] = function*() {
        order = order.map( prop => this[ prop ] );
        yield* order;
      };
      return this;
    }

Мы создаем Proxy с именем sortable и предоставляем наш обработчик Object и get :

  let sortable = new Proxy( obj, {
    get: function( target, prop, receiver ) {
      ...
    }
  });

В обработчике get мы добавляем условие, которое говорит, что , если имя свойства, которое мы ищем, является методом нашего частного объекта , мы привязываем Object к этому методу и вызываем его.

Ae Если мы получаем Object["a"], это дает нам Object["a"], но если мы пытаемся получить Object["sort"], это дает нам private.sort.bind(Object)

Примечание: это закрытие делает наши методы приватными :

  let sortable = new Proxy( obj, {
    get: function( target, prop, receiver ) {
      if ( private[ prop ] ) {
        return private[ prop ].bind( sortable );
      }
      return target[ prop ];
    }
  });

Наконец мы вызываем нашу sort функцию наш Proxy для установки нашего итератора по умолчанию, так как прототипы объектов не имеют его изначально:

  sortable.sort( Object.keys( obj ) );

и возвращают прокси:

  return sortable;

Объяснение в глубину использования:

Поскольку у нас есть Конструктор , который принимает Object, мы можем обернуть наш определенный объект при его инициализации:

  obj = ObjectSortable( {
    "a": 1,
    "b": 2,
    "c": 3
  });

Затем мы применяем наш Кондит ional logi c с использованием нашей функции sort:

if ( condition ) {
  obj.sort( [ "a", "b", "c" ] );
} else {
  obj.sort( [ "a", "c", "b" ] );
}

Наконец, мы перебираем элементы , которые находятся в правильном порядке:

for(let item of obj) {
   console.log(item);
}

Внутренние элементы Symbol.iterator позволяют нам легко вычислять l oop по результатам, используя for...of l oop, но мы также можем помещать их в массив при необходимости, используя оператор распространения:

let orderedArray = [...obj];

Из приведенного выше принуждения мы можем l oop использовать Array методы, такие как forEach, map, reduce, et c.

Лично я рекомендовал бы создать свой собственный метод итерации


Как добавить forEach:

Поскольку мы уже используем конструктор , определяющий наш собственный итератор и предоставление частных методов, , мы можем очень легко расширить private с помощью метода forEach.

  const private = {
    forEach: function(fn) {
      for (let value of this) {
        fn(value);
      }
    },
    sort: function(order) {
      ...
    }
  }

Наш метод forEach принимает Function как единственный параметр. Затем функция вызывается для каждого значения , предоставленного итератором .

Отличительной особенностью в вышеприведенном является то, что из-за нашей динамической привязки c в нашем обработчике get , мы можем просто сказать forEach методу итерировать по текущий контекст.

Это приведет к меньшему коду по сравнению с for...of l oop или Принуждению массива и его легче поддерживать с функциональной точки зрения.


для каждого примера:

function ObjectSortable(obj = {}) {
  const private = {
    forEach: function(fn) {
      for (let value of this) {
        fn(value);
      }
    },
    sort: function(order) {
      this[Symbol.iterator] = function*() {
        order = order.map(prop => this[prop]);
        yield* order;
      };
      return this;
    }
  }

  let sortable = new Proxy(obj, {
    get: function(target, prop, receiver) {
      if (private[prop]) {
        return private[prop].bind(sortable);
      }
      return target[prop];
    }
  });
  sortable.sort(Object.keys(obj));
  return sortable;
}

let test = ObjectSortable();
test.a = "world";
test.b = "hello";

test.sort(["b","a"]);
test.forEach(console.log)

forEach в вашей демоверсии:

function ObjectSortable(obj = {}) {
  const private = {
    forEach: function(fn) {
      for (let value of this) {
        fn(value);
      }
    },
    sort: function(order) {
      this[Symbol.iterator] = function*() {
        order = order.map(prop => this[prop]);
        yield* order;
      };
      return this;
    }
  }

  let sortable = new Proxy(obj, {
    get: function(target, prop, receiver) {
      if (private[prop]) {
        return private[prop].bind(sortable);
      }
      return target[prop];
    }
  });
  sortable.sort(Object.keys(obj));
  return sortable;
}


var condition = false,
  obj = ObjectSortable( {
    "a": 1,
    "b": 2,
    "c": 3
  } );
  
if ( condition ) {
  obj.sort( [ "a", "b", "c" ] );
} else {
  obj.sort( [ "a", "c", "b" ] );
}

obj.forEach(console.log);

Заключение:

Надеюсь, это дало вам представление о некоторых внутренних элементах JavaScript объектов и о том, как вещи могут соединяться друг с другом, используя вещи как шаблон частного метода. Я надеюсь, что это решит вашу проблему!

Счастливого кодирования!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...