2011-07-06 11 views
21

Estoy tratando de iterar sobre todos los globales definidos en un sitio web, pero al hacerlo también obtengo las funciones nativas del navegador.Detectar si la función es nativa del navegador

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); 
    } 
} 

¿Hay alguna forma de determinar si una función es nativa del navegador o está creada en un script?

Respuesta

12

Puede llamar a la función heredada .toString() en los métodos y verificar el resultado. Los métodos nativos tendrán un bloque como [native code].

if(this[p].toString().indexOf('[native code]') > -1) { 
    // yep, native in the browser 
} 

actualización, porque muchos de los comentaristas quieren alguna aclaración y la gente realmente tener un requisito para tal detección. Para realizar esta comprobación realmente ahorrar, probablemente deberíamos utilizar una línea de línea de esto:

if(/\{\s+\[native code\]/.test(Function.prototype.toString.call(this[ p ]))) { 
    // yep, native 
} 

Ahora estamos utilizando el método de la .toStringprototype de Function lo que hace muy improbable, si no imposible algún otro guión ha sobrescrito la toString método. En segundo lugar, estamos comprobando con una expresión regular, por lo que no podemos dejarnos engañar por los comentarios dentro del cuerpo de la función.

+0

Esto sólo funcionaría si toString no había sido anulado, no? – joekarl

+2

@jAndy ¿Es esto infalible? Pensé que 'toString' no funciona en todos los navegadores modernos o algo así. – William

+3

@joekit si se sobreescribe 'toString' usted debería poder hacer' Function.prototype.toString.call (obj) .indexOf ('[native code]'); 'También probablemente sería una mejor idea usar RegExp. Intente llamar a la función contra sí mismo, y se vería como ** nativo ** porque aparece en la cadena. – William

10
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))); 
} 

esto debería ser lo suficientemente bueno. esta función realiza las siguientes pruebas:

  1. null o undefined;
  2. el param es en realidad una función;
  3. el parámetro es en sí mismo Function.prototype (este es un caso especial, donde Function.prototype.toString da function Empty(){})
  4. el cuerpo de la función es exactamente function <valid_function_name> (<valid_param_list>) { [native code] }

la expresión regular es un poco complicado, pero lo que realmente funciona bastante decentemente rápido en cromo en mi portátil Lenovo 4 GB (core duo):

var n = (new Date).getTime(); 
for (var i = 0; i < 1000000; i++) { 
    i%2 ? isFuncNative(isFuncNative) : 
      isFuncNative(document.getElementById); 
}; 
(new Date).getTime() - n; 

3023ms. por lo que la función tarda alrededor de 3 microsegundos para ejecutarse una vez que todo está JIT.

Funciona en todos los navegadores. Anteriormente, utilicé Function.prototype.toString.call, esto bloquea IE, ya que en IE, los métodos de elemento DOM y los métodos de ventana NO son funciones, sino objetos, y no tienen el método toString. El constructor de cadenas resuelve el problema elegantemente.

-1

Intenté un enfoque diferente. Esto solo se prueba para Firefox y 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's 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 
+0

Devuelve 'falso' para' Promesa' aunque mi navegador admite promesas de forma nativa. – mpen

+0

Sí, no sé por qué es eso. Promise.prototype.constructor no es igual a Object.prototype.constructor. Por lo general, no pruebo si es nativo de todos modos, y busco el método "entonces", que es como las promesas nativas resuelven las promesas de la biblioteca de cualquier forma. Si todo lo demás falla, entonces uso 'Promise.resolve (nonNative)' para normalizar todas las promesas. –

+0

Principalmente funciona en Chrome. Devuelve falso para 'RegExp'. – trusktr

0

casi todos ellos se producirá un error, debido a que:

function notNative(){ 
this.toString = "[native code]"; 
} 

lugar:

Function.prototype.isNative = function(){ 
 
return Function.prototype.toString.call(this).slice(-14, -3) === "native code"; 
 
}; 
 

 
console.log(alert.isNative()); 
 
console.log(String.isNative()); 
 
function foo(){} 
 
console.log(foo.isNative());

+0

¿Puede explicar por qué su solución es mejor y cómo funciona? – rene

Cuestiones relacionadas