Расшифруйте, пожалуйста, функцию. Комментарии будут очень полезны

Изучал сортировку таблицы и вот попался такой код. Код этот я уже скомпилировал через babel (до этого он был еще менее читаемым). Эта функция позволяет отсортировать <table> при клике <th> конкретной колонки (банальный addEventListener).

function getCellValue(tr, index) {
  return tr.children[index].innerText;
};

function comparer(index, asc) {
  return function (rowA, rowB) {
    return (function (prevTd, nextTd) {
      return prevTd.toString().localeCompare(nextTd);
    })(
      getCellValue(asc ? rowA : rowB, index),
      getCellValue(asc ? rowB : rowA, index)
    );
  };
};

Array.from(table.querySelectorAll("tr:nth-child(n+2)"))
  .sort(
    comparer(
      Array.from(target.parentNode.children).indexOf(target),
      (this.asc = !this.asc)
    )
  )
  .forEach(function (tr) {
    return table.appendChild(tr);
  });

Загвоздка больше всего в comparer. Можно ли ее сделать проще без высшего порядка? В частности непонятен этот блок:

return (function (prevTd, nextTd) {
  return prevTd.toString().localeCompare(nextTd);
})(
  getCellValue(asc ? rowA : rowB, index),
  getCellValue(asc ? rowB : rowA, index)
);

Выше после первого return стоит скобка, затем getCellValue (2 шт) тоже помещены в скобки. Что это значит? Зачем вообще использованы return в return, который тоже находится в return? Почему getCellValue(asc ?...) используется дважды? В общем, вопросов больше чем ответов. Прокомментируйте, пожалуйста?


Ответы (1 шт):

Автор решения: Alex Krass

Есть функции, результатом выполнения которых являются другие функции и очень часто используются. Самый простой пример дается через возведение в степень.

function expFabric (exp) {
  return function(value){
    return value ** exp;
  }
}

// Получаем другую функцию, в который уже фиксированный exp
let square = expFabric(2); 
console.log(square(10));
console.log(square(20));

// Получаем другую функцию, в который уже фиксированный exp
let cube = expFabric(3);
console.log(cube(10));
console.log(cube(20));

Функции можно вызывать сразу после объявления, код аналогичен вышеприведенному.

let square = (function expFabric (exp) {
  return function(value){
    return value ** exp;
  }
})(2); // сразу передаем два и получаем функцию внутри

console.log(square(10));
console.log(square(20));

Ваш compare генерирует другую функцию:

let array = Array.from(table.querySelectorAll("tr:nth-child(n+2)"));
let anotherFunction = comparer(
    Array.from(target.parentNode.children).indexOf(target),
    (this.asc = !this.asc)
);
array.sort(anotherFunction)
     .forEach(function (tr) {
       return table.appendChild(tr);
     });

Получается в anotherFunction у нас лежит функция.

function (rowA, rowB) {
  return (function (prevTd, nextTd) {
    return prevTd.toString().localeCompare(nextTd);
  })(
    getCellValue(asc ? rowA : rowB, index),
    getCellValue(asc ? rowB : rowA, index)
  );
};

Аргументы в эту функцию передает сама sort, когда мы ей передаем anotherFunction; Эта функция как раз вызывается сразу с аргументами. Можно переписать в более понятный код.

Итерация 1:

function compareString (prevTd, nextTd) {
    return prevTd.toString().localeCompare(nextTd);
};

function (rowA, rowB) {
  return compareString (
    getCellValue(asc ? rowA : rowB, index),
    getCellValue(asc ? rowB : rowA, index)
  );
};

Или итерация 2:

function compareString (prevTd, nextTd) {
    return prevTd.toString().localeCompare(nextTd);
};

function (rowA, rowB) {
  let left = getCellValue(asc ? rowA : rowB, index);
  let right = getCellValue(asc ? rowB : rowA, index);
  return compareString (left, right);
};

Ну и полная картина на более понятных вещах:

function getCellValue(tr, index) {
  return tr.children[index].innerText;
};

function compareString (prevTd, nextTd) {
  return prevTd.toString().localeCompare(nextTd);
};
    
function comparer(index, asc) {
  return function (rowA, rowB) {
    let left = getCellValue(asc ? rowA : rowB, index);
    let right = getCellValue(asc ? rowB : rowA, index);
    return compareString (left, right);
  };
};

let index = Array.from(target.parentNode.children).indexOf(target);
let asc = (this.asc = !this.asc);
let currentComparer = comparer(index, asc);

Array.from(table.querySelectorAll("tr:nth-child(n+2)"))
     .sort(currentComparer)
     .forEach(function (tr) {
       return table.appendChild(tr);
     });

P.S.> поскольку это абстрактый код без примера, то возможны ошибки, но думаю общая логика понятная.

→ Ссылка