Seleccionar página
Recientemente nos hemos encontrado con la necesidad de identificar si una determinada función es una función nativa del lenguaje Javascript o ha sido creada por código. La cuestión parece sencilla, pero cuando nos ponemos a pensar como resolverlo vemos que en principio las funciones y métodos escritos por nosotros en Javascript y los proporcionados con por el lenguaje no se diferencian a primera vista. Las funciones nativas se pueden incluso reescribir por otras nuestras, por lo que no tienen ningún privilegio o diferencia que podamos observar fácilmente.

Ya nos enfrentamos a un problema similar cuando nos preguntamos cómo diferenciar arrow function de function. En esa ocasión descubrimos dos formas de actuar, analizando el prototipo (que finalmente resultó infructuosa) y analizando la cadena devuelta por .toString()

Lamentablemente no hay nada identificable en la cadena de prototipos para localizar las funciones nativas. Es cierto que las funciones nativas no tienen prototype, ya que no pueden ser utilizadas como constructores, pero tampoco lo tienen los métodos de nuestras clases o las arrow function. Por aquí no vamos a encontrar la solución.

Por lo tanto, no nos queda más remedio que analizar el código devuelto por .toString(). Comprobamos en diferentes navegadores y motores que siempre incluye la cadena [native code]:

console.log (parseInt.toString ());
 

function parseInt() { [native code] }

Por lo tanto, con una pequeña comprobación tenemos un sencillo medio para identificar si una función es nativa o está construida por código.

function isNative(fn) {
  return fn.toString().includes('[native code]');
}

Como primera aproximación no está mal, pero cualquier código que contenga el texto '[native code]' dará positivo. Es cierto que no es un texto muy habitual, pero nada impide que ese texto aparezca en el cuerpo de una función. Por ejemplo, si preguntamos si la propia función isNativeFunction() es nativa, obtendremos un falso positivo:

console.log(isNativeFunction(isNativeFunction)); // true

Con poco esfuerzo adicional podemos hacer una comprobación un poco más completa, incluyendo si es una función, las llaves y los posibles espacios en blanco entre los elementos, el final de la cadena y el propio nombre de la función.

function isNativeFunction (fn) {
  const str = fn.toString ();
  return typeof (fn) === 'function' &&
         /\{\s*\[\s*native code\s*\]\s*\}$/.test (str) &&
         str.includes (fn.name);
}
 

Ahora se puede ejecutar una batería de pruebas con diferentes opciones y comprobar que todo funciona correctamente:

console.assert (!isNativeFunction (function () {}));
console.assert (!isNativeFunction (function* () {}));
console.assert (!isNativeFunction (() => {}));

class C {m() {}}
console.assert (!isNativeFunction (C));
console.assert (!isNativeFunction (C.prototype.m));
console.assert (!isNativeFunction (isNativeFunction));
console.assert (!isNativeFunction ({
  toString () {
    return `function x { [native code] }`;
  }
}));
console.assert (!isNativeFunction (
  function x () {
    return "function x { [native code] }";
  }
));

Pero, ¿es seguro hacer uso de .toString()?. Lo cierto es que el estándar Javascript ha sido un poco confuso en cuanto a lo que tiene que devolver la función .toString() en el caso de las funciones. No obstante, todos los navegadores y motores de Javascript han realizado una implementación muy similar, aunque con mínimas variaciones.

Recientemente se ha propuesto una revisión de la estandarización de toString() para que no tenga ambigüedades, esta propuesta se encuentra en estos momentos en Stage 4, es decir, preparado para formar parte del estándar en la próxima edición. Por ello podemos estar bastante tranquilos en su utilización.

Aunque pueda parecer una solución un poco indirecta, lo cierto es que funciona. Podemos estar tranquilos al consultar el código de la función para analizar su tipología. Este es un recurso que tenemos a nuestra disposición y con la estandarización de facto y la nueva revisión de la estandarización formal de ECMAScript es seguro utilizarlo.

Novedades

Datos inmutables en Javascript

Datos inmutables en Javascript

En Javascript todo parece mutable, es decir, que se puede cambiar, pero lo cierto es que también nos ofrece varios mecanismos para conseguir que los datos que manejamos, especialmente los objetos, sean inmutables. Te invitamos a descubrir cómo…

Copiar objetos en Javascript

Copiar objetos en Javascript

Copiar objetos no es algo sencillo, incluso se podría decir que en si mismo no es posible, ya que el concepto «copiar» no entra dentro del paradigma de los objetos. No obstante, por medio de instrucciones como Object.assign() hemos aprendido como obtener objetos con las mismas propiedades, pero está técnica no se puede aplicar a todos los tipos de objetos disponibles en Javascript. Vamos a ver cómo podemos copiar cualquier tipo de objeto…

Descubre los Javascript Array Internals

Descubre los Javascript Array Internals

El Array es una de las estructuras más utilizadas en Javascript y no siempre bien comprendida. Hoy os invitamos a analizar el comportamiento interno de este objeto y descubrir cómo Javascript implementa las diferente acciones con los Array y que operaciones internas se realizan en cada caso.

Web Components: pasado, presente y futuro

Web Components: pasado, presente y futuro

Los Web Components aparecieron en el panorama de desarrollo hace ya bastante tiempo. Desde su presentación se les ha prestado mucha atención, pero lo cierto es que no han sido acogidos de forma generalizada, quizás por la difusión de nuevos y potentes frameworks. Nos preguntamos qué ha pasado con este estándar y, sobre todo, que puede pasar de aquí en adelante con el uso práctico de los componentes web.