javascript - ecmascript - ¿Cómo verificar si una variable es una declaración de clase ES6?
es6 javascript (4)
Estoy exportando la siguiente clase de ES6 desde un módulo:
export class Thingy {
hello() {
console.log("A");
}
world() {
console.log("B");
}
}
Y importándolo desde otro módulo:
import {Thingy} from "thingy";
if (isClass(Thingy)) {
// Do something...
}
¿Cómo puedo verificar si una variable es una clase? ¿No es una instancia de clase, sino una declaración de clase?
En otras palabras, ¿cómo implementaría la función isClass
en el ejemplo anterior?
Aclararé aquí, cualquier función arbitraria puede ser un constructor. Si está distinguiendo entre "clase" y "función", está tomando malas decisiones de diseño de API. Si asume que algo debe ser una class
por ejemplo, nadie que use Babel o Typescript será detectado como una class
porque su código se habrá convertido en una función. Significa que está obligando a que cualquier persona que use su base de código debe estar ejecutándose en un entorno ES6 en general, por lo que su código será inutilizable en entornos más antiguos.
Sus opciones aquí están limitadas al comportamiento definido por la implementación. En ES6, una vez que se analiza el código y se procesa la sintaxis, no queda mucho comportamiento específico de la clase. Todo lo que tienes es una función constructora. Tu mejor opción es hacer.
if (typeof Thingy === ''function''){
// It''s a function, so it definitely can''t be an instance.
} else {
// It could be anything other than a constructor
}
y si alguien necesita realizar una función que no sea de constructor, exponga una API separada para eso.
Obviamente, esa no es la respuesta que está buscando, pero es importante que quede claro.
Como lo menciona la otra respuesta aquí, tiene una opción porque .toString()
en las funciones es necesario para devolver una declaración de clase, por ejemplo
class Foo {}
Foo.toString() === "class Foo {}" // true
La clave, sin embargo, es que eso solo se aplica si es posible . Es 100% compatible con las especificaciones para que una implementación tenga
class Foo{}
Foo.toString() === "throw SyntaxError();"
Actualmente, ningún explorador hace eso, pero hay varios sistemas integrados que se centran en la programación de JS, por ejemplo, y para preservar la memoria de su programa, descartan el código fuente una vez que se ha analizado, lo que significa que no tendrán ningún código fuente para devolver. .toString()
y eso está permitido.
De manera similar, al utilizar .toString()
usted está haciendo suposiciones sobre el diseño de API en general y en el futuro. Di que haces
const isClass = fn => /^/sclass/.test(fn.toString());
porque esto se basa en representaciones de cadena, podría romperse fácilmente.
Tomemos por ejemplo a los decoradores:
@decorator class Foo {}
Foo.toString() == ???
¿El .toString()
de esto incluye al decorador? ¿Qué pasa si el decorador devuelve una function
lugar de una clase?
Qué pasa:
function isClass(v) {
return typeof v === ''function'' && v.prototype.constructor === v;
}
Si desea asegurarse de que el valor no solo sea una función, sino que realmente sea una función constructora para una clase, puede convertir la función en una cadena e inspeccionar su representación. La especificación dicta la representación de cadena de un constructor de clase .
function isClass(v) {
return typeof v === ''function'' && /^/s*class/s+/.test(v.toString());
}
Otra solución sería intentar llamar al valor como una función normal. Los constructores de clases no se pueden llamar como funciones normales, pero los mensajes de error probablemente varían de un navegador a otro:
function isClass(v) {
if (typeof v !== ''function'') {
return false;
}
try {
v();
return false;
} catch(error) {
if (/^Class constructor/.test(error.message)) {
return true;
}
return false;
}
}
La desventaja es que invocar la función puede tener todo tipo de efectos secundarios desconocidos ...
Tal vez esto pueda ayudar
let is_class = (obj) => {
try {
new obj();
return true;
} catch(e) {
return false;
};
};