Как в JavaScript определить, является ли функция нативной
Время от времени мне приходится проверять, является ли та или иная
функция нативной — это важная часть проверки, была ли функция предоставлена
браузером или это порождение постороннего шима, замаскированное под встроенный
компонент. Лучший способ выполнения такой проверки — это, конечно же, оценка
значения toString
, возвращённого функцией.
JavaScript
Код, требуемый для этого, довольно прост:
function isNative(fn) {
return (/\{\s*\[native code\]\s*\}/).test('' + fn);
}
Вся суть состоит в том, чтобы конвертировать функцию в строчное представление и
выполнить сопоставление строки с регулярными выражениями.
Лучшего способа подтвердить, что это нативная функция, не существует!
Обновление!
Создатель библиотеки lodash, Джон-Дэвид Далтон (John-David Dalton), предложил лучшее решение:
;(function() {
// Используется для разложения на составляющие внутреннего `[[Class]]` значений
var toString = Object.prototype.toString;
// Используется для разложения на составляющие декомпилированного
// исходного кода функции
var fnToString = Function.prototype.toString;
// Используется для определения конструкторов среды (Safari > 4;
// по сути, предназначено специально для типизированных массивов)
var reHostCtor = /^\[object .+?Constructor\]$/;
// Составление регулярного выражения на основе часто употребляемого
// нативного метода в качестве шаблона.
// Выбираем `Object#toString`, так как вполне вероятно, что он ещё не задействован.
var reNative = RegExp('^' +
// Применяем `Object#toString` к строке
String(toString)
// Избавляемся от любых специальных символов регулярных выражений
.replace(/[.*+?^${}()|[\]\/\\]/g, '\\$&')
// Заменяем упоминания `toString` на `.*?`, чтобы сохранить обобщённый вид шаблона.
// Заменяем `for ...` и тому подобное для поддержки окружений вроде Rhino,
// которые добавляют дополнительную информацию, такую как арность метода.
.replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
);
function isNative(value) {
var type = typeof value;
return type == 'function'
// Используем `Function#toString`, чтобы обойти собственный метод
// `toString` самого значения и избежать ложного результата.
? reNative.test(fnToString.call(value))
// На всякий случай выполняем проверку на наличие объектов среды, так
// как некоторые окружения могут представлять компоненты вроде
// типизированных массивов как методы DOM, что может не соответствовать
// нормальному нативному паттерну.
: (value && type == 'object' && reHostCtor.test(toString.call(value))) || false;
}
// экспортируем в удобном для вас виде
module.exports = isNative;
}());
И вот теперь у нас точно есть лучший подход для определения, является ли метод нативным. Естественно, не стоит использовать его для обеспечения безопасности — это лишь признак того, что функция нативна.