Обнаруживать, является ли функция родной для браузера
Я пытаюсь перебрать все глобальные значения, определенные на веб-сайте, но при этом я также получаю собственные функции браузера.
var numf=0; var nump=0; var numo=0;
for(var p in this) {
if(typeof(this[p]) === "function"){
numf+=1;
console.log(p+"()");
} else if(typeof p != 'undefined'){
nump+=1;
console.log(p);
} else {
numo+=1;
console.log(p);
}
}
Есть ли способ определить, является ли функция родной для браузера или создана в script?
Ответы
Ответ 1
Вы можете вызвать унаследованную функцию .toString()
для методов и проверить результат. У родных методов будет такой блок, как [native code]
.
if( this[p].toString().indexOf('[native code]') > -1 ) {
// yep, native in the browser
}
Обновление, потому что многие комментаторы хотят получить некоторые пояснения, и у людей действительно есть требование для такого обнаружения. Чтобы эта проверка действительно сохранялась, мы, вероятно, должны использовать линию:
if( /\{\s+\[native code\]/.test( Function.prototype.toString.call( this[ p ] ) ) ) {
// yep, native
}
Теперь мы используем метод .toString
из prototype
of Function
, что делает его очень маловероятным, если не невозможно, какой-то другой script заменил метод toString
. Во-вторых, мы проверяем регулярное выражение, поэтому мы не можем обманываться комментариями внутри тела функции.
Ответ 2
function isFuncNative(f) {
return !!f && (typeof f).toLowerCase() == 'function'
&& (f === Function.prototype
|| /^\s*function\s*(\b[a-z$_][a-z0-9$_]*\b)*\s*\((|([a-z$_][a-z0-9$_]*)(\s*,[a-z$_][a-z0-9$_]*)*)\)\s*{\s*\[native code\]\s*}\s*$/i.test(String(f)));
}
это должно быть достаточно хорошо. эта функция выполняет следующие тесты:
- null или undefined;
- параметр на самом деле является функцией;
- параметр является самим свойством Function.prototype(это особый случай, когда Function.prototype.toString дает
function Empty(){}
)
- тело функции точно
function <valid_function_name> (<valid_param_list>) { [native code] }
regex немного сложнее, но на самом деле он работает довольно быстро в chrome на моем ноутбуке lenovo емкостью 4 ГБ (дуо-ядро):
var n = (new Date).getTime();
for (var i = 0; i < 1000000; i++) {
i%2 ? isFuncNative(isFuncNative) :
isFuncNative(document.getElementById);
};
(new Date).getTime() - n;
3023ms. поэтому функция запускается где-то около 3 микросекунд для запуска, когда все JIT'ed.
Он работает во всех браузерах. Раньше я использовал Function.prototype.toString.call, это приводит к сбою IE, поскольку в IE методы элемента DOM и методы окна являются НЕ функциями, а объектами, и у них нет метода toString. Конструктор строк решает проблему элегантно.
Ответ 3
Function.prototype.toString
может быть подделан, что-то вроде этого:
Function.prototype.toString = (function(_toString){
return function() {
if (shouldSpoof) return 'function() { [native code] }'
return _toString.apply(this, arguments)
}
})(Function.prototype.toString)
Вы можете определить, был ли Function.prototype.toString
разграблен, поймав в ловушку .apply()
, .call()
, .bind()
(и других).
И если это так, вы можете получить "чистую" версию Function.prototype.toString
из недавно введенного IFRAME
.
Ответ 4
Я попробовал другой подход. Это проверяется только на firefox и chrome.
function isNative(obj){
//Is there a function?
//You may throw an exception instead if you want only functions to get in here.
if(typeof obj === 'function'){
//Check does this prototype appear as an object?
//Most natives will not have a prototype of [object Object]
//If not an [object Object] just skip to true.
if(Object.prototype.toString.call(obj.prototype) === '[object Object]'){
//Prototype was an object, but is the function Object?
//If it not Object it is not native.
//This only fails if the Object function is assigned to prototype.constructor, or
//Object function is assigned to the prototype, but
//why you wanna do that?
if(String(obj.prototype.constructor) !== String(Object.prototype.constructor)){
return false;
}
}
}
return true;
}
function bla(){}
isNative(bla); //false
isNative(Number); //true
isNative(Object); //true
isNative(Function); //true
isNative(RegExp); //true
Ответ 5
почти все из них потерпит неудачу, потому что:
function notNative(){}
notNative.toString = String.bind(0, "function notNative() { [native code] }");
console.log( notNative.toString() );