Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Помогите пожалуйста, если можно с объяснением (https://javascript.ru/forum/misc/83414-pomogite-pozhalujjsta-esli-mozhno-s-obyasneniem.html)

DenisZlgst 02.12.2021 21:02

Помогите пожалуйста, если можно с объяснением
 
Не могу решить задачу.


Написать функцию, которая принимает в качестве параметра объект и
возвращает массив методов принимаемого объекта.

рони 02.12.2021 21:44

DenisZlgst,
:cray:
const foo = obj => Object.keys(obj);

https://developer.mozilla.org/ru/doc...ts/Object/keys

voraa 02.12.2021 22:17

Цитата:

Сообщение от рони
const foo = obj => Object.keys(obj);

Ну не....
Это будет массив всех свойств, а нужны методы

const foo = obj => Object.keys(obj).filter(m => typeof obj[m] === 'function')


Но все равно непонятно какие именно методы нужны. Тут вернутся только собственные перечислимые. Еще могут быть и не перечислимые, и методы прототипов... И заданные не строками, а символами (Symbol)... В том числе и в прототипах.

Aetae 03.12.2021 02:43

Вот те универсальная шляпа.)
/**
 * @callback Filter
 * @param value
 * @param {string | symbol} key
 * @param {?object} getset
 * @param {?function} getset.get
 * @param {?function} getset.set
 * @returns {boolean}
 */

/**
 * @typedef Options
 * @type {object}
 * @property {boolean=true} deep
 * @property {boolean=true} getters
 * @property {boolean=getters} setters
 * @property {boolean=false} symbols
 * @property {boolean=} enumerable
 * @property {boolean=} configurable
 * @property {boolean=} writable
 */

/**
 * @param {object} obj
 * @param {Filter} [filter]
 * @param {Options} [options]
 * @returns {string[]}
 */

function getPropertyKeys(obj, filter, options) {
  if (typeof filter !== 'function') {
    options = filter;
    filter = (value, key) => true;
  }

  const {
    deep = true,
    getters = true,
    setters = getters,
    symbols = false,
    ...descriptorOptions
  } = options ?? {};

  const not = (descriptor, key) => key in descriptorOptions && descriptorOptions[key] !== descriptor[key];
  
  const {
    getOwnPropertySymbols,
    getPrototypeOf,
    getOwnPropertyDescriptors,
    keys,
    assign,
    prototype
  } = Object;
  
  let current = obj;
  let descriptors;

  do {
    descriptors = assign(getOwnPropertyDescriptors(current), descriptors);

    if (current === prototype) break;

    current = getPrototypeOf(current);
  } while (deep && current);

  let list = keys(descriptors);
  if (symbols)
    list = list.concat(getOwnPropertySymbols(descriptors));

  return list.filter(key => {
    const descriptor = descriptors[key];
    const { get, set, value } = descriptor;

    if (not(descriptor, 'enumerable'))
      return false;

    if (not(descriptor, 'configurable'))
      return false;
    
    if (get || set) {
      return (setters || setters) 
      	&& filter(get && get.call(obj), key, { get, set });
    }
    
    if (not(descriptor, 'writable'))
      return false;

    return filter(value, key);
  });
}

getPropertyKeys([], value => typeof value === 'function', {
  symbols: true
});

рони 03.12.2021 07:18

Aetae,
:)

рони 03.12.2021 07:23

Aetae,
строки 32 - 34 верно или надо поменять местами?

Aetae 03.12.2021 07:38

рони, верно, это была фишка со свапом: [a, b] = [b, a], чтоб типа obj [,filter [, options]]. Я конечно намудрил там излишне, поправил по-человечски.)

рони 03.12.2021 07:40

Aetae,
ясно.

voraa 03.12.2021 07:45

Цитата:

Сообщение от Aetae
это фишка со свапом: [a, b] = [b, a], чтоб типа obj [,filter [, options]].

Ну обычно так делают, если какой то параметр просто можно пропустить.
Либо (object, filter, options)
либо (object, options)
Менять местами - излишество.
Хотя правильнее, наверно, (object, options, filter)

рони 03.12.2021 07:47

Aetae,
интересная фишка!!! +++

Aetae 03.12.2021 07:50

voraa, не согласен на счёт (object, options, filter). В options нормальные умолчания.)

Правилбьно было бы конечно, чтоб и так (object, options, filter) и так (object, filter, options). Но это некрасивую условную раскоряку писать.)

Давно хочу либу сделать для работы с аргументами функции в любых порядках и направлениях, да руки не доходят. Те что видел в npm мне эстетически не нравятся.)

voraa 03.12.2021 08:22

Цитата:

Сообщение от Aetae
Давно хочу либу сделать для работы с аргументами функции в любых порядках и направлениях, да руки не доходят.

Я тоже пытался. Но отказался, так как часто получается слишком тяжеловесно.
Простой пример - функция создания элемента
С вариантами использования
create (tag, options [, children [, parent]])
create (tag [, children | null [, parent]])
где
tag - string
children - Node | Array | string
parent - Element
options - object (но не Array | Node)

Слишком много разных проверок, что бы просто создать элемент Проще зафиксировать параметры и проверять на null или undefined.
Универсализм плохо сочетается с эффективностью.

Aetae 03.12.2021 14:02

voraa, я это себе как-то так представляю(чисто черновой набросок):
function create ({tag, options, children, parent}) {
  console.log({tag, options, children, parent})
}


function isType(arg, types) {
  if(Array.isArray(types))
    return types.some(type => isType(arg, type));
  if(typeof types === 'function')
    return types(arg);
  return arg === types;
}
function findArgs(args, list) {
  let maxArgsMatchLength = -1;
  let maxArgsMatchNames;
  list.forEach(({
    sublist,
    sublist: { length },
    names
  }) => {
    if(length <=  maxArgsMatchLength) return; // ничего не делаем если уже нашли больше аргументов
    for(let i = length; i--;) {
      if(!isType(args[i], sublist[i])) 
        return false;
    }
    maxArgsMatchLength = length;
    maxArgsMatchNames = names;
  });

  if(maxArgsMatchLength === -1)
    throw new Error('invalid arguments');

  return Object.fromEntries(
    maxArgsMatchNames
    .slice(0, maxArgsMatchLength)
    .map((key, i) => [key, args[i]])
  )
}

function withAttrs(func, list) {
  const bigList = list.flatMap(obj => {
    const sublist = [];
    const entries = Object.entries(obj);
    const current = [];
    const names = [];

    for(let arg in obj) {
      const type = obj[arg];
      if(arg.endsWith('?')) {
        arg = arg.slice(0, -1);
        sublist.push(current.slice());
      }
      names.push(arg);
      current.push(type)
    }
    sublist.push(current);

    return sublist.map(arr => ({ sublist: arr, names }));
  })


  return function(...args) {
    return func.call(this, findArgs(args, bigList)); 
  }
}

isNode = () => true;

createWithArgs = withAttrs(create, [
  {tag: isString, options: isPlainObject, 'children?': [isNode, isArray, isString], 'parent?': isElement},
  {tag: isString, 'children?': [isNode, isArray, isString, null], 'parent?': isElement},
]);

createWithArgs('1', {a: 3}, [])


Все isX функции из lodash, но естественно возможно использование своих.
+Обязательно должно бы кэширование: чтоб одна функция проверки типа вызывалась для одного конкретного аргумента только один раз.
+Разделить проверку списка аргументов не всех сразу, а по порядку.
+Добавить поддержку spread.
+Типизировать для typescript(это будет ад, если вообще возможно).
...
=)

voraa 03.12.2021 17:08

Это конечно жесть и ад.

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

Vlasenko Fedor 03.12.2021 20:52

Все это называется код ради кода.
Если вы это используете то у вас ошибки в архитектуре, голове ...

Aetae 04.12.2021 00:20

Vlasenko Fedor,
Цитата:

Все это называется код ради кода.
Так здесь я этим и занимаюсь. Код ради дела - он на работе и в пет-проектах.)
Цитата:

Если вы это используете
Нет конечно.)

Просто иногда пишешь функцию с чётким интерфейсом, а мыслишка в голове "а ведь было бы круто если бы можно было и так, и вот так, и ещё так и чтоб TS сам на это overload'ы вывел".)
С TS кстати это не настолько ужасно, как может показаться: он должен проверять как возможные комбинации аргументов относительно переданной функции, так и делать чёткий интерфейс для новой. Т.е. тут нельзя будет опечататься или указать что-то не то. Как вариант обёртки для внешнего интерфейса какой-нить либы - почему нет.)

Vlasenko Fedor 04.12.2021 02:22

Цитата:

Сообщение от Aetae
Просто иногда пишешь функцию с чётким интерфейсом

автоматное програмирование здесь бы запилить, но дорого будет :lol:
мне кажется так достаточно для парметров
const test = (tag, params = {}) => {
  const def = {
    a: 'text',
    b: 2
  }
  params = { ...def, ...params }
  console.log(params)
}

test('tag', { b: 5, c: 3 })

без использования TS, глянув дефолтные всегда можно понять, что передать
TS не учу, я им vbs, vb не могу простить
только подумал MS продуктами уже 12 лет как и не пользуюсь и вроде ок :)

voraa 04.12.2021 12:30

Aetae,
Для ускорения строку 21 наверно можно так

if (length != args.length) return;

Aetae 04.12.2021 15:14

voraa, не, мыж не знаем что там за проверки, может ему ок, если последние аргументы будут undefined:
{foo: isString, bar: [isArray, undefined], 'buz': [isElement, undefined]},

По идее конечно в таком случае надо юзать 'foo?' но мало ли какие там сложные и хитрые проверки будут.)

voraa 04.12.2021 15:45

По идее
('str')
и
('str', undefined, undefined)
- разные вещи.
В последнем случае количество аргументов будет 3 и проверка нормально сработает

ЗЫ Я так нарвался на scrollIntoView()
у меня там вызов получался scrollIntoView(x), а x было = undefined
И долго чесал репу, почему не работает, как надо

Aetae 06.12.2021 05:19

Ну так то да. Но тогда if (length > args.length) return; потому что можно передать левый мусор в отсутствующие аргументы и это будет ок.)

voraa 06.12.2021 06:44

Я попытался повозиться с твоим скриптом, адаптировать его для собственных нужд. Работает нормально. Но столкнулся с небольшой неувязкой.
return func.call(this, findArgs(args, bigList));
предполагает, что функция может быть и методом.
Но super из нее не вызовешь.

С проверками типов тоже приходится быть очень аккуратным
[
{par1: isObject, 'par2?': isArray},
{par2: isArray}
]

isObject просто как o => typeof o === 'object' не сделаешь
Надо o => typeof o === 'object' && !Array.isArray(o)
Ну и других подобных ситуаций хватало. Поэтому проверки типов иногда становятся довольно громоздкими

Меня такие коды бросают в дрожь из-за цены вызова функции по времени.
Провел тестирование
Вызывал 1 000 000 раз обычную функцию (но пустую) и делал вызов с такими проверками параметров.
С одной стороны разница по времени в 50 раз!
С другой стороны 0.6 сек на миллион вызовов.

Aetae 06.12.2021 07:19

Дык, всё в туду списке указано.
1. Решается обработкой по порядку, т.е. напишешь так:
voraa,
[
{par2: isArray},
{par1: isObject, 'par2?': isArray}
]
И если первое прошло, на второе не пойдёт. Там чутка поправить надо, убрать flatmap и добавить ещё один цикл.)
Ну и isPlainObject.

2. Кэширование. Как в рамках одного вызова, так и в рамках всех вызовов в таске.

Ну и 50 раз оверхед над пустой функцией не особо страшен, потому что он статичен. Возьми функцию по-тяжелее и процент оверхеда уменьшится.)

А для особо критичных ситуаций - надо предусмотреть возможность вызова оригинальной функции.

3. super и не будет работать в функциях, а если ты обернёшь метод и положишь его обратно - всё будет норм.

voraa 06.12.2021 08:20

Цитата:

Сообщение от Aetae
а если ты обернёшь метод и положишь его обратно - всё будет норм.

Не там вообще все непросто
Это все прокатывает только для статических методов. И что бы пользоваться super приходится делать два метода - не обернутый и обернутый

class A extends B {
....
static f ({args...}) {
 super.f()
}
static fwa = withAttrs(f, [...])
}

просто
static f = withAttrs(function ({args}) {
 super.f() // !!!
}, [.....])

не сделаешь

А не статический метод просто в классе не сделать, только через прототип туда его пихать

Приходит в голову мысль получать то, что bigList отдельно, а саму функцию метод оформлять так

method (...args) {
   let {par1, par2, par3} = findArgs(args, bigList);
   .....
}

Aetae 06.12.2021 09:03

Вообще всегда так делали, коли надо:
constructor(){
  super();
  this.method = withAttrs(this.method); 
}

Ток в ts, такое не прокатит, да.

voraa 06.12.2021 10:44

Цитата:

Сообщение от Aetae
Ток в ts, такое не прокатит, да.

ts тут ни при чем.
И в обычном js это как то не так.
Методы должны быть в прототипе, а не в объекте.

Aetae 06.12.2021 14:31

voraa, ну
A.prototype.method = withAttrs(A.prototype.method);
тож сработает.
А ts тут при том что очень не любит когда классы трогают уже после декларации.)


Часовой пояс GMT +3, время: 06:58.