Как стать автором
Обновить

Комментарии 32

Зная это, можно написать функцию, позволяющую различать нативные и пользовательские функции

isNative(isNative) => true

Люблю javascript ^^

Точно. А почему так?

Потому что в исходнике isNative присутствует постройка, по которой определяется нативность.

НЛО прилетело и опубликовало эту надпись здесь

Потому что строчка native code внезапно есть в тексте функции isNative :)
Корректная реализация видимо должна как-то так проверять:


function isNative (Ctor){
  return typeof Ctor === 'function' && /\{\s*\[native code\]\s*\}$/.test(Ctor.toString())
}
isNative.toString = function() { 
    return parseInt.toString().replace(parseInt.name, this.name ); 
}
isNative(isNative) => true

Ну это всё-таки намеренное стреляние в ногу. Фишка с bind от B_bird более прикольная, показывает что определить точно, будет ли вызываться пользовательский код невозможно.

Это скорее ошибка логики в написании функции
// Used to resolve the internal `[[Class]]` of values
const toString = Object.prototype.toString;

// Used to resolve the decompiled source of functions
const fnToString = Function.prototype.toString;

// Used to detect host constructors (Safari > 4; really typed array specific)
const reHostCtor = /^\[object .+?Constructor]$/;

// Compile a regexp using a common native method as a template.
// We chose `Object#toString` because there's a good chance it is not being mucked with.
const reNative = RegExp('^' +
  // Coerce `Object#toString` to a string
  String(toString)
    // Escape any special regexp characters
    .replace(/[.*+?^${}()|[\]/\\]/g, '\\$&')
    // Replace mentions of `toString` with `.*?` to keep the template generic.
    // Replace thing like `for ...` to support environments like Rhino which add extra info
    // such as method arity.
    .replace(/toString|(function).*?(?=\\\()| for .+?(?=\\])/g, '$1.*?') + '$'
);

const isNative = value => {
  const type = typeof value;
  return type === 'function'
    // Use `Function#toString` to bypass the value's own `toString` method
    // and avoid being faked out.
    ? reNative.test(fnToString.call(value))
    // Fallback to a host object check because some environments will represent
    // things like typed arrays as DOM methods which may not conform to the
    // normal native pattern.
    : (value && type === 'object' && reHostCtor.test(toString.call(value))) || false;
};

isNative(isNative); // Покажет false
isNative(Symbol); // Покажет true

Всё равно


isNative(function(){}.bind(this)) // true

Беда, беда.

Различение встроенных и пользовательских функций

Только не забывать про использование bind:
const a = function() { console.log(1); }
a.toString()
"function() { console.log(1); }"
a.bind(this).toString()
"function () { [native code] }"

Реализация cached неудачная: нули, пустые строки, false, null и undefined функция кэшировать не будет, а будет каждый раз вычислять

Специалисты, поясните, пожалуйста, а почему тяжёлая функция не может сама позаботиться о кэшировании своих результатов без обёртки:

fn.chache = [ {} ];

function fn( arg )  {
  let chachedResult= fn.chache.find( ( x ) => { return x.arg === arg } );
  if ( chachedResult )
      return chachedResult.result;

  let result;
  ... //  вычисление result

  fn.chache.push( { arg: arg, result: result } );
  return result;
}

Тем более тут нет проблемы с null, undefined.

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

let chachedResult= fn.chache.find( ( x ) => { return x.arg === arg } );

Зачем вам тут O(n)? Сделайте кеш с использованием new Map или new WeakMap.


кэшировании своих результатов без обёртки

Ну вы собственно сделали обёртку, просто не в виде метода, а в виде кода выше и кода ниже самих вычислений. Суть та же самая. От перестановки мест слагаемых...

Потому что инвалидация кэша — одна из основных проблем программирования. Предположения автора функции сделали бы ее менее гибкой.

А еще неудачное имя, так как это принято называть мемоизацией или memoization. А если правильно назвать, то смотри, можно и кучу готовых и проверенных годами решений найти, lodash например.

Осторожнее с утечками памяти. Кажется по-дефолту там используется {} в качестве хранилища. Код какой-то сильно путанный, бегло пробежался. Предлагают использовать weakMap в качестве альтернативы, но тут имейте ввиду что ничего кроме object-ов в качестве ключей тогда использовать будет нельзя.

Ну не то чтобы прям утечка памяти, просто по-умолчанию он не чистится. Жаль в JS нет встроенной возможности сделать кеш из серии "ну можно чистить, когда памяти не хватает". В любом случае, нужно использовать с умом: если вы знаете что функция хоть и тяжелая, но будет выпоняться для ограниченного набора элементов — почему бы и нет. Вот, скажем, у меня есть инструмент, где учитель может видеть результаты работы учеников. Максимальное количество закешированных элементов равняется количеству учеников. Не то чтобы стоит переживать.


А вот если речь идет об сложной обработке сложных строк, которые идут из пользовательского ввода, т.е. набор элементов может быть неограничен — стоит позаботиться о том, чтобы хранить только немного элементов (иногда вообще только последний результат).

В любом случае, нужно использовать с умом

Имхо проще написать свои велоспеды чем использовать такой _.memoize. А ещё есть много вменяемых альтернатив на любой вкус и цвет (даже с использованием proxy dependency detection).

Серьезно, открытие в 2020 году кэша функции ???? А вот про типы было интересно!
Функция с кешем была бы лучше, если предыдущий falsey результат не вызывал рассчет заново.
export function cached<F: Function> (fn: F): F {
const cache = Object.create(null)
return (function cachedFn (str: string) {
if (!cache.hasOwnProperty(str)) cache[str] = fn(str);
return cache[str];
}: any)
}
if ( !cache[str] )

Что здесь, что во Vue одна и та же грубая ошибка: если исходная функция вернет любое «false» значение — кэш не сработает.
Как все мы знаем, в JavaScript существует шесть примитивных типов данных (Boolean, Number, String, Null, Undefined, Symbol) и один объектный тип — Object.
С каких это пор Null является типом данных? Почему нету тогда типа NaN?
Интересно, не знал что в стандарте он описуется именно как отдельный тип, хотя на практике, значение null является типом Object, так сказать без ссылки на сам объект.
Результат typeof null == "object" – это официально признанная ошибка в языке, которая сохраняется для совместимости.

Ну и на практике сплошь и рядом реальные типы в программе number|null, string|null и иногда даже boolean|null, а не только object|null

По сути все что вы перечислили — это хорошо известные паттерны разработки или полезные функции.

Их существует очень много:
JavaScript паттерны проектирования 1
JavaScript паттерны проектирования 2
JavaScript — полезные функции

и т.д.
  1. Определение точного типа любого объекта

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

Думаю, что первые 4 позиции тут-там периодически всем нам от проекта к проекту приходится юзать/реализовывать, и, в принципе, ничего особо интересного в этих находках нет, более того, как подметили некоторые комментаторы — есть более грамотные решения.

Зная это, можно написать функцию, позволяющую различать нативные и пользовательские функции

Ни разу не требовалось подобного, но очень хотелось бы увидеть реальный пример из практики. Заранее благодарю )
Ну едва ли это можно называть интересным- типичные подходы, коим более 10 лет… toString еще и весьма сомнительный из-за производительности.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий