Vácuo
Não recomendo tentar definir ou usar uma função que calcule se algum valor no mundo inteiro está vazio. O que realmente significa ser "vazio"? Se eu tiver let human = { name: 'bob', stomach: 'empty' }, devo isEmpty(human)voltar true? Se eu tiver let reg = new RegExp('');, devo isEmpty(reg)voltar true? Que isEmpty([ null, null, null, null ])tal - esta lista contém apenas o vazio, então a lista em si está vazia? Quero apresentar aqui algumas notas sobre "vacuidade" (uma palavra intencionalmente obscura, para evitar associações pré-existentes) em javascript - e quero argumentar que "vacuidade" em valores de javascript nunca deve ser tratada genericamente.
Veracidade / falsidade
Para decidir como determinar a "vacuidade" dos valores, precisamos acomodar o senso inerente e inerente ao javascript de saber se os valores são "verdadeiros" ou "falsos". Naturalmente, nulle undefinedambos são "falsos". Menos naturalmente, o número 0(e nenhum outro número, exceto NaN) também é "falso". Menos naturalmente: ''é falso, mas []e {}(e new Set(), e new Map()) são verdadeiros - embora todos pareçam igualmente vazios!
Nulo vs Indefinido
Há também alguma discussão sobre nullvs undefined- nós realmente precisamos de ambos para expressar a vacuidade em nossos programas? Pessoalmente, evito que as letras u, n, d, e, f, i, n, e, d apareçam no meu código nessa ordem. Eu sempre uso nullpara significar "vazio". Novamente, porém, precisamos acomodar o senso inerente de javascript de como nulle undefineddiferir:
- Tentar acessar uma propriedade inexistente fornece
undefined
- Omitir um parâmetro ao chamar uma função resulta nesse parâmetro recebendo
undefined :
let f = a => a;
console.log(f('hi'));
console.log(f());
- Parâmetros com valores padrão recebem o padrão somente quando fornecidos
undefined, não null:
let f = (v='hello') => v;
console.log(f(null));
console.log(f(undefined));
Vacuidade não genérica
Acredito que a vacuidade nunca deve ser tratada de maneira genérica. Em vez disso, devemos sempre ter o rigor para obter mais informações sobre nossos dados antes de determinar se estão vazios - eu faço isso principalmente verificando com que tipo de dados estou lidando:
let isType = (value, Cls) => {
try {
return Object.getPrototypeOf(value).constructor === Cls;
} catch(err) {
return false;
}
};
Observe que essa função ignora o polimorfismo - espera valueser uma instância direta de Cls, e não uma instância de uma subclasse de Cls. eu evitoinstanceof por duas razões principais:
([] instanceof Object) === true ("Uma matriz é um objeto")
('' instanceof String) === false ("Uma String não é uma String")
Observe que Object.getPrototypeOfé usado para evitar um caso como let v = { constructor: String };A isTypefunção ainda retorna corretamente para isType(v, String)(false) e isType(v, Object)(true).
No geral, eu recomendo usar esta isTypefunção junto com estas dicas:
- Minimize a quantidade de valores de processamento de código de tipo desconhecido. Por exemplo, pois
let v = JSON.parse(someRawValue);, nossa vvariável agora é do tipo desconhecido. O mais cedo possível, devemos limitar nossas possibilidades. A melhor maneira de fazer isso pode ser exigindo um tipo específico: por exemplo if (!isType(v, Array)) throw new Error('Expected Array');- essa é uma maneira muito rápida e expressiva de remover a natureza genérica ve garantir que seja sempre uma Array. Às vezes, porém, precisamos permitir vque sejam de vários tipos. Nesses casos, devemos criar blocos de código onde vnão são mais genéricos, o mais cedo possível:
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');
}
- Sempre use "listas brancas" para validação. Se você precisar que um valor seja, por exemplo, String, Number ou Array, verifique essas 3 possibilidades "brancas" e gere um Erro se nenhuma das 3 estiver satisfeita. Devemos ser capazes de ver que a verificação de possibilidades "negros" não é muito útil: dizer que escrever
if (v === null) throw new Error('Null value rejected');- isso é ótimo para garantir que nullvalores não passar por isso, mas se um valor faz passar por isso, ainda sabemos pouco nada sobre isso. Um valor vque passa nessa verificação nula ainda é MUITO genérico - é tudo menosnull ! As listas negras dificilmente dissipam a genérica.
A menos que seja um valor null, nunca considere "um valor vazio". Em vez disso, considere "um X que é vazio". Essencialmente, nunca considere fazer algo assim if (isEmpty(val)) { /* ... */ }- não importa como essa isEmptyfunção seja implementada (eu não quero saber ...), não é significativo! E é genérico demais! A vacuidade deve ser calculada apenas com o conhecimento do valtipo. As verificações de vacuidade devem ter a seguinte aparência:
- "Uma string, sem caracteres":
if (isType(val, String) && val.length === 0) ...
- "Um objeto, com 0 adereços":
if (isType(val, Object) && Object.entries(val).length === 0) ...
- "Um número igual ou menor que zero":
if (isType(val, Number) && val <= 0) ...
"Uma matriz, sem itens": if (isType(val, Array) && val.length === 0) ...
A única exceção é quando nullé usado para significar determinada funcionalidade. Nesse caso, é significativo dizer: "Um valor vazio":if (val === null) ...