Показать сообщение отдельно
  #1 (permalink)  
Старый 05.06.2018, 19:15
Интересующийся
Отправить личное сообщение для Yesasha Посмотреть профиль Найти все сообщения от Yesasha
 
Регистрация: 07.01.2018
Сообщений: 15

Задача на хитрость. Обёртка метода. Apply. Bind. Function length.
Здравствуйте. Пишу обёртку для стандартных джаваскрипт функций. Возникает много вопросов, некоторые удаётся разрешить самому, но совет профессионалов был бы очень кстати.

Как правильно сделать обёртку для метода?
Например есть метод Array.prototype.slice, который часто используется для преобразования объекта к массиву.
Можно так:
function slice (obj, begin, end) {
  return Array.prototype.slice.apply(obj, Array.prototype.slice.call(arguments, 1));
}

Тут нам нужно избавиться от первого аргумента и для этого нужно преобразовать аргументы к массиву, это медленно.
Можно так:
function slice (obj, begin, end) {
  // Можно взять метод и непосредственно с функции, но для наглядности оставлю полную версию
  return Function.prototype.call.apply(Array.prototype.slice, arguments);
}

Тут возникает вопрос, поддерживает ли метод .apply arraylike объекты? Информация по этому вопросу расплывчата. Если нет, то придётся приводить к массиву, что в конечном итоге сведёт на нет оптимизацию.
Может быть можно в рантайме определять поддержку и выдавать нужный вариант? Но это сложно и увеличит кол-во кода.
Есть ещё вариант с .bind, но с его поддержкой тоже есть некоторые проблемы, плюс этот вариант не сохранит .length:
var slice = Function.prototype.call.bind(Array.prototype.slice);

Усложняем задачу.
Есть такая вещь как https://github.com/es-shims/es-shim-api
  • In every way possible, the package must attempt to make itself robust against the environment being modified after it is required.
  • For example, require('foo'); delete Function.prototype.call; must not alter the behavior of foo.
  • The most useful technique for this is shown in this example: var bind = require('function-bind'); var slice = bind.call(Function.call, Array.prototype.slice); slice([1], 1); — this technique works in ES3 environments, and will ensure that modifying Array.prototype will not interfere with the package.
В общем проблемы такие, нам советуют использовать .bind, но делая функции на экспорт, надо сохранить .length, а с биндом .length теряется. А без .bind, без бубна никак. Хотя с бубном кое что получилось:
var Function_prototype = Function.prototype;
var Function_prototype_call = Function_prototype.call;
var Function_prototype_apply = Function_prototype.apply;
var Function_prototype_bind = Function_prototype.bind;

// Удаляем все методы из прототипа
Function_prototype.call = null;
Function_prototype.apply = null;
Function_prototype.bind = null;

// Что дальше?
// Что то типа такого
var Array_prototype_slice = Array.prototype.slice;
function slice (obj, begin, end) {
  // Восстановим .apply непосредственно на функции
  Function_prototype_call.apply = Function_prototype_apply;
  return Function_prototype_call.apply(Array_prototype_slice, arguments);
  delete Function_prototype_call.apply; // Удалим
}

Есть идеи получше?
Ответить с цитированием