Vacío
No recomiendo tratar de definir o usar una función que calcule si algún valor en todo el mundo está vacío. ¿Qué significa realmente estar "vacío"? Si tengo let human = { name: 'bob', stomach: 'empty' }, debería isEmpty(human)volver true? Si tengo let reg = new RegExp('');, debería isEmpty(reg)volver true? ¿Qué pasa isEmpty([ null, null, null, null ])? Esta lista solo contiene el vacío, entonces, ¿está vacía la lista misma? Quiero presentar aquí algunas notas sobre "vacuidad" (una palabra intencionalmente oscura, para evitar asociaciones preexistentes) en javascript, y quiero argumentar que la "vacuidad" en los valores de javascript nunca debe tratarse genéricamente.
Verdad / falsedad
Para decidir cómo determinar la "vacuidad" de los valores, necesitamos acomodar el sentido inherente e inherente de JavaScript de si los valores son "verdaderos" o "falsos". Naturalmente, nully undefinedambos son "falsos". Menos naturalmente, el número 0(y ningún otro número excepto NaN) también es "falso". Menos natural: ''es falso, pero []y {}(y new Set(), y new Map()) son verdaderos, ¡aunque todos parecen igualmente vacíos!
Nulo vs Indefinido
También hay una discusión acerca de nullvs undefined: ¿realmente necesitamos ambos para expresar vacío en nuestros programas? Personalmente, evito que las letras u, n, d, e, f, i, n, e, d aparezcan en mi código en ese orden. Siempre uso nullpara significar "vacuidad". Una vez más, sin embargo, necesitamos acomodar el sentido inherente de JavaScript de cómo nully undefineddiferir:
- Intentar acceder a una propiedad inexistente da
undefined
- Omitir un parámetro al llamar a una función da como resultado que ese parámetro reciba
undefined:
let f = a => a;
console.log(f('hi'));
console.log(f());
- Los parámetros con valores predeterminados reciben el valor predeterminado solo cuando se proporcionan
undefined, no null:
let f = (v='hello') => v;
console.log(f(null));
console.log(f(undefined));
Vacío no genérico
Creo que la vacuidad nunca debe tratarse de manera genérica. En su lugar, siempre deberíamos tener el rigor de obtener más información sobre nuestros datos antes de determinar si son vacíos; principalmente hago esto verificando qué tipo de datos estoy tratando:
let isType = (value, Cls) => {
try {
return Object.getPrototypeOf(value).constructor === Cls;
} catch(err) {
return false;
}
};
Tenga en cuenta que esta función ignora el polimorfismo: espera valueser una instancia directa de Cls, y no una instancia de una subclase de Cls. Evito instanceofpor dos razones principales:
([] instanceof Object) === true ("Una matriz es un objeto")
('' instanceof String) === false ("Una cadena no es una cadena")
Tenga en cuenta que Object.getPrototypeOfse utiliza para evitar un caso como let v = { constructor: String };La isTypefunción todavía regresa correctamente para isType(v, String)(falso) y isType(v, Object)(verdadero).
En general, recomiendo usar esta isTypefunción junto con estos consejos:
- Minimice la cantidad de valores de procesamiento de código de tipo desconocido. Por ejemplo, para
let v = JSON.parse(someRawValue);, nuestra vvariable ahora es de tipo desconocido. Tan pronto como sea posible, debemos limitar nuestras posibilidades. La mejor manera de hacerlo puede ser exigiendo un tipo particular: por ejemplo if (!isType(v, Array)) throw new Error('Expected Array');, esta es una forma realmente rápida y expresiva de eliminar la naturaleza genérica de v, y garantizar que siempre sea así Array. A veces, sin embargo, debemos permitir vser de múltiples tipos. En esos casos, debemos crear bloques de código donde vya no sea genérico, lo antes posible:
if (isType(v, String)) {
/* v isn't generic in this block - It's a String! */
} else if (isType(v, Number)) {
/* v isn't generic in this block - It's a Number! */
} else if (isType(v, Array)) {
/* v isn't generic in this block - it's an Array! */
} else {
throw new Error('Expected String, Number, or Array');
}
- Utilice siempre "listas blancas" para la validación. Si necesita que un valor sea, por ejemplo, una cadena, un número o una matriz, verifique esas 3 posibilidades "blancas" y arroje un error si ninguno de los 3 está satisfecho. Deberíamos poder ver que verificar las posibilidades "negras" no es muy útil: digamos que escribimos
if (v === null) throw new Error('Null value rejected');, esto es excelente para garantizar que los nullvalores no se logren, pero si un valor se logra , todavía no lo sabemos nada al respecto. Un valor vque pasa esta verificación nula sigue siendo MUY genérico, ¡es todo menosnull eso ! Las listas negras apenas disipan el genérico.
A menos que un valor sea null, nunca considere "un valor vacío". En cambio, considere "una X que está vacía". Esencialmente, nunca considere hacer algo como if (isEmpty(val)) { /* ... */ }: no importa cómo isEmptyse implemente esa función (no quiero saber ...), ¡no tiene sentido! ¡Y es demasiado genérico! La vacuidad solo debe calcularse con conocimiento del valtipo. Los controles de vacío deberían tener este aspecto:
- "Una cadena, sin caracteres":
if (isType(val, String) && val.length === 0) ...
- "Un objeto, con 0 accesorios":
if (isType(val, Object) && Object.entries(val).length === 0) ...
- "Un número, igual o menor que cero":
if (isType(val, Number) && val <= 0) ...
"Una matriz, sin elementos": if (isType(val, Array) && val.length === 0) ...
La única excepción es cuando nullse usa para indicar cierta funcionalidad. En este caso es significativo decir: "Un valor vacío":if (val === null) ...