Underscore: _.isNumber (NaN) retorna verdadeiro

Criado em 15 dez. 2011  ·  38Comentários  ·  Fonte: jashkenas/underscore

Como NaN significa "Não é um número", a verificação isNumber, neste caso, parece que deve retornar falso. Percebo em outras discussões que isso é de fato proposital. Talvez a documentação deva refletir esse fato explicitamente.

enhancement fixed

Comentários muito úteis

Ok, então comecei com um problema simples. Tenho entradas que são strings, números e possivelmente NaN. Eu tenho uma solução simples em JavaScript básico utilizando parseInt e isFinite para testar se a análise int é bem-sucedida. Simples, mas não totalmente claro ou descritivo do meu objetivo. Então, eu decido usar minha biblioteca goto totalmente incrível para fazer esse tipo de tarefa. Eu encontro uma função na inspeção inicial que diz que leva um valor e diz se é um número, que pela minha definição comum é o que eu acho que quero.

Primeiro, com isso você pode ver como adicionar uma linha à documentação ajudaria os usuários da biblioteca a obter a função certa para o trabalho na primeira vez? Em segundo lugar, peço novamente um exemplo em que isso seja significativo.

Parece que você sabe tudo o que há para saber sobre o idioma. Então, ao invés de lançar de volta soluções que meu irmão da 8ª série poderia me dar, talvez fosse bom se você pudesse usar um pouco dessa instrução de maestria para o resto de nós por meio de documentação impressionante. Obrigado pelo seu tempo.

Todos 38 comentários

Você está certo, é de propósito. E não acho que isso precise ser declarado explicitamente. Qualquer pessoa que já trabalhou com flutuadores IEEE 754 antes provavelmente sabe que NaN é apenas outro valor de ponto flutuante.

Relacionado: discussão em # 321

O que quero dizer é que, quando estou realizando tarefas comuns da Web, uma função chamada isNumber parece uma boa maneira de verificar se um valor encontrado responde à definição comum do usuário de "isso é um número?".

Além disso, parece-me que o valor NaN foi introduzido em primeiro lugar para ajudar a identificar valores que quebram os cálculos. No espírito das especificações, parece ser a resposta correta quando você faz a pergunta "isso é um número?" é na verdade que é "Não é um número". Se você puder citar um exemplo comum em que deseja que a resposta seja verdadeira para esta pergunta, calo a boca agora :)

Você está procurando por isFinite , que as pessoas que conhecem JS já devem saber.

Ok, então comecei com um problema simples. Tenho entradas que são strings, números e possivelmente NaN. Eu tenho uma solução simples em JavaScript básico utilizando parseInt e isFinite para testar se a análise int é bem-sucedida. Simples, mas não totalmente claro ou descritivo do meu objetivo. Então, eu decido usar minha biblioteca goto totalmente incrível para fazer esse tipo de tarefa. Eu encontro uma função na inspeção inicial que diz que leva um valor e diz se é um número, que pela minha definição comum é o que eu acho que quero.

Primeiro, com isso você pode ver como adicionar uma linha à documentação ajudaria os usuários da biblioteca a obter a função certa para o trabalho na primeira vez? Em segundo lugar, peço novamente um exemplo em que isso seja significativo.

Parece que você sabe tudo o que há para saber sobre o idioma. Então, ao invés de lançar de volta soluções que meu irmão da 8ª série poderia me dar, talvez fosse bom se você pudesse usar um pouco dessa instrução de maestria para o resto de nós por meio de documentação impressionante. Obrigado pelo seu tempo.

Estou interessado no que @jashkenas pensa. Não quero parecer um insulto ou condescendente. Eu sei que às vezes pareço assim. Eu acho que é o fato de que estou tentando conduzir a conversa em uma direção que mostre melhor o problema, dando essas respostas simples e vendo como elas falham em resolver o problema em seus olhos.

Primeiro, com isso você pode ver como adicionar uma linha à documentação ajudaria os usuários da biblioteca a obter a função certa para o trabalho na primeira vez?

Possivelmente, com certeza.

Em segundo lugar, peço novamente um exemplo em que isso seja significativo.

Testando para [[Class]] == "Número"? Isso deveria ser óbvio, corresponde a números. Talvez não sejam números naturais ou inteiros ou números finitos ou números reais ou números racionais ou qualquer subconjunto que você esperava, mas com certeza são todos números.

Dito isso, há momentos em que adicionar muitos detalhes à documentação pode ser prejudicial. Mas não acho que esse seja um desses momentos. Isso pode ser potencialmente útil para desenvolvedores que podem, em seu estado atual, ter uma interpretação conflitante do conceito abstrato de um número. Como no seu caso, onde você já estabeleceu um modelo mental do conjunto para o qual deseja testar e, em seguida, encontrou uma função que _pareceu_ para realizar esse teste. +1 , embora ainda esteja interessado no que @jashkenas pensa.

Sim - não é nada óbvio se _.isNumber retornará true ou false quando passado em NaN . Certamente não me lembraria sem tentar. Adicionada uma nota à documentação no commit acima. Obrigado.

Obrigado rapazes. Fico feliz em saber que outra pessoa pode não ter esse mesmo problema.

Esse definitivamente parece um caso em que a semântica deve superar os fatos técnicos. Sim, NaN pode ser um número de acordo com algumas especificações. Mas se eu fizer algo, a pergunta "Você é um número?" e diz "Não sou um número", então devo acreditar. Não é um número - NaN - definitivamente não é um número ... e isNumber deve retornar falso.

Colocar o Gotcha nos documentos - em vez de consertar as coisas para fazer sentido para os humanos - apenas leva a menos tempo de codificação e mais tempo examinando documentação coçando sua cabeça.

Isso pode ser reconsiderado?

@contentfree : NaN é um membro dos carros alegóricos. Cada número em JS é um float. Eu acho que isso torna NaN mais numeroso possível. NaN não significa "Não sou um número". É a representação numérica de não números. Caso encerrado em meu livro. Use isFinite se quiser testar os números que não são NaN / Infinity / -Infinity .

Para não bater em um cavalo morto, mas ...

Por que não apenas fazer
obj instanceOf Number

Parece-me que se existe uma forma mais legível de fazer a ação necessária que já existe na especificação do javascript, farei dessa forma, sem usar uma biblioteca com uma biblioteca desconhecida (a menos que perca tempo investigando).

Eu voto que, se não vamos consertar isso para que seja semanticamente correto, devemos apenas remover a sobrecarga inútil e a dor de cabeça da biblioteca.

O operador instanceof só funcionará para objetos, não primitivos: new Number(5) instanceof Number == true; 5 instanceof Number == false . Ele também não funcionará em frames nos navegadores. [[Class]] verificação via Object::toString é geralmente aceito como o método de verificação de tipo mais confiável em JavaScript.

+1 em tornar a biblioteca mais útil e, ao mesmo tempo, educar os usuários novos em JavaScript.

Sugestão:
_.isNumber (objeto, [isFinite]);

Isso permitirá uma adição simples de um verdadeiro à sua implementação atual quando for pego por essa pegadinha enquanto aprende que isFinite era provavelmente o que você procurava em primeiro lugar.

Meu 2c

@ nickl- Idéia interessante, mas ... por que não usar simplesmente isFinite nesse caso?

Alternativamente, como _.isNaN já existe, _.isFinite poderia ser adicionado para simetria:

_.isFinite = function (value) {
  return value > -1 / 0 && value < 1 / 0;
};

@kitcambridge

O único problema que prevejo é que as strings seriam passadas como finitas "0x0", "0xF", "2", etc. Portanto, não importa o que aconteça, ele precisará ser combinado com _.isNumber ou equivalente:

_.isFinite = function (obj) {
  return obj > -1/0 && obj < 1/0 && _.isNumber(obj);
};

Pode cortar cinco caracteres usando val === + val para teste de número:

_.isFinite = function (obj) {
  return obj > -1/0 && obj < 1/0 && obj === +obj;
};

@octatone Claro, acho que é justo ... tecnicamente, essas strings _são_ finitas, pois podem ser usadas em comparações numéricas (o interpretador deve forçá-las a números), mas sua proposta é mais consistente com as funções de verificação de tipo de sublinhado existentes.

E quanto a _, isValidNumber que está em linha com o _.isValidDate existente e não confunde o argumento isFinite?

@ nickl-

NaN e Infinity _are_ números válidos. Acho que nomear esta função isValidNumber confundiria seu propósito ou você quer dizer renomear isNumber para isValidNumber?

NaN e Infinity _are_ números válidos. Acho que nomear essa função como isValidNumber confundiria seu propósito.

Eu concordo.

Eu não disse renomear ...

Também fiquei surpreso ao descobrir que _.isNumber(NaN) === true .

Acho que tendo a concordar com https://github.com/jashkenas/underscore/issues/406#issuecomment -4144992 de @ contentfree. Na engenharia de software, temos até um princípio para este caso específico: Princípio da menor surpresa . ;-)

E depois de ver tantas pessoas confusas sobre isso, acho que faria sentido fazer a função fazer o que humanos não hard-core-js-coders esperam que ela faça quando olham para seu nome.

Apenas meu pequeno 2 ¢… ™

_.isNaN também é confuso. No documento:

Observação: não é o mesmo que a função isNaN nativa, que também retornará true para muitos outros valores não numéricos, como undefined.

no sentido normal, undefined é de fato Não-Um-Número.

Eu voto pela construtividade semântica, isNumber == Not a number lol. Eu acredito, se você precisar entrar em detalhes sobre como verificar os valores de ponto flutuante, faça isso de outra maneira?

Eu voto pela construtividade semântica,

Não, é um número [[Class]] assim como -Infinity , Infinity , & Object(2) . É uma daquelas coisas que os desenvolvedores aprendem, como se as funções também fossem objetos. É provável que você queira fazer alguma forma de validação em seu número que está fora do escopo deste método. Por exemplo, é maior que -1 , menor que Math.pow(2,32) ou um número inteiro. Nos casos de -Infinity , Infinity ou NaN eu uso _.isFinite como o validador. Da mesma forma, _.isDate não valida se o objeto de data representa uma data válida.

Que tal atualizar a documentação com "veja também: isFinite" e "veja também:« ensaio que enumera métodos para determinar se algo é um valor numérico que você pode realmente usar »"

@michaelficarra Eu sei que na especificação NaN é muito mais um tipo numérico. Mas é isso que um programador está pensando quando pergunta se isso é um número?

O que a maioria de nós quer saber, na maioria das vezes, é: posso usar isso para aritmética básica. Então você tem que ir isNumber (x) && isFinite (x). E está tudo bem, eu acho. Mas é um grande problema para um novo programador e não lê bem.

Do ponto de vista estritamente da língua inglesa, Not a Number (NaN) retornar true de um teste chamado isNumber não faz sentido. Não seria melhor se chamar isNumeric ou isNumericType?

Não tenho dúvidas de que isso adicionará bugs e perderá muitas horas, pelo menos na primeira vez que as pessoas se depararem com isso.

Então você tem que ir isNumber (x) && isFinite (x).

Recentemente, alinhei uma implementação de _.isFinite para seguir ES6 Number.isFinite .
Ele garante que o valor é um número primitivo, é finito e deve lidar com esse caso comum.

Não há necessidade de alterar o comportamento de _.isNumber que se alinha com os outros [[Class]] métodos de verificação.

Do ponto de vista estritamente da língua inglesa, Not a Number (NaN) retornar true de um teste chamado isNumber não faz sentido. Não seria melhor se chamar isNumeric ou isNumericType?

Veja meu comentário sobre validação, _.isNumber e _.isDate .

@jdalton Você está correto sobre o comportamento ser constante com _.isDate. E vocês fazem um ótimo trabalho com o sublinhado, então acho que a decisão realmente cabe a vocês.

Mas parece tão contra-intuitivo que algo chamado, não um número, seja um número.

@Walms 100% concorda. Acho que, neste caso, o programador e a intuição devem ignorar a "correção" absoluta da declaração dada NaN é _tecnicamente_ numérica.

Mas parece tão contra-intuitivo que algo chamado, não um número, seja um número.

Nesse contexto, é mais contra-intuitivo do que _.isNumber retornar true para objetos numéricos, Object(2) que são do tipo object ?

Estes são métodos [[Class]] . Talvez isso precise ficar mais claro na documentação.

Já propus uma alternativa viável que é simplesmente fazer _.isFinite seguir ES6 Number.isFinite . Se você quiser um is-number básico, use typeof x == 'number' . Se você quiser alguma validação de que o valor não é NaN , Infinity , ou -Infinity , todos [[Class]] de Number , então _.isFinite é o caminho a percorrer.

Observe a contribuição de sublinhado para métodos de números mais refinados. Tem _.isNumeric , _.isInteger , _.isZero , _.isEven , _.isOdd , _.isFloat , _.isNegative , & _.isPositive .

@jdalton Acho que seus dois últimos comentários realmente ajudaram a esclarecer porque acho isso confuso.

Inicialmente, não vi que _.isNumber recebeu um contexto pelo fato de ser prefixado por is, o que significa que era uma verificação de tipo. Com esse contexto, você está totalmente correto, não faz sentido _.isNumber retornar falso para NaN.

Mas o sublinhado-contrib e o isFinite parecem quebrar esse contexto. Como eles começam, ainda estão olhando para o valor em vez do tipo. E é aí que eu acho que a confusão é que não há uma maneira clara de determinar o contexto a partir do nome da função.

Tudo isso dito, não vejo nenhuma maneira de consertar isso.

Acho que o problema aqui é que existem algumas coisas sobre o Numbers que não são intuitivas. No entanto, eles se comportam de maneira consistente. Seria possível tornar _.isNumber mais intuitivo neste caso, tornando-o menos consistente, o que, em última análise, o tornaria menos intuitivo em outros casos.

Aqui estão alguns exemplos concretos:

# If you add two Numbers together you always get another Number back
# This function should always return true no matter what you pass it
closedUnderAddition = (a, b) -> !isNumber(a) || !isNumber(b) || isNumber(a + b)

closedUnderAddition(1,1) == true
closedUnderAddition(Number.MAX_INT,2**970) == true # false if isNumber checks finiteness

# If you divide two Numbers you always get another Number back
closedUnderDivision = (a, b) -> !isNumber(a) || !isNumber(b) || isNumber(a / b)

closedUnderDivision(1, 2) == true
closedUnderDivision(1, 0) == true # false if isNumber checks finiteness
closedUnderDivision(0, 0) == true # false if isNumber checks finiteness or NaN-ness

# Anything you cast to a Number is a Number
castsToNumber = (x) -> isNumber(Number(x))

castsToNumber(1) == true
castsToNumber(Infinity) == true # false if isNumber checks finiteness
castsToNumber("bees") == true # false if isNumber checks finiteness or NaN-ness
castsToNumber("3") == true

Como disse michaelficarra:

NaN não significa "Eu não sou um número". É a representação numérica de não números.

Ou, dito de outra forma, existem "números" e Números. NaN pode não ser um "número", mas é absolutamente um Número junto com o Infinito, -Infinito e coisas ridículas como -0. Por mais selvagem e confuso que seja, no entanto, a definição de Número é bem especificada e se comporta de maneira consistente.

"número", por outro lado, é um conceito mal definido que, dependendo de com quem você está falando, pode incluir qualquer um ou nenhum dos itens meia -

Acho que, por esse motivo, este é essencialmente um problema de documentação.

Além disso, "número" parou de parecer uma palavra para qualquer outra pessoa?

Pontos positivos, @sgentle. Eu voto pela consistência em vez da intuitividade, já que a primeira é uma medida objetiva, ao passo que nem todos concordaremos sobre o que é intuitivo (como este tópico demonstra).

Apenas não trate NaN como "Não é um Número". NaN é um valor especial.

Apenas não trate NaN como "Não é um Número". NaN é um valor especial.

Sim, mas é chamado de "Não é um número".
Para mim é assim
var _false = "verdadeiro";
Sim, você pode aprender que _false é verdade, mas é confuso sem um bom motivo.

@Walms É um nome ruim, mas não é culpa de JS. Podemos culpar o cara que chamou de "NaN". http://en.wikipedia.org/wiki/NaN

Argh, eu gostaria que _.isNumber(NaN) retornasse false ... alguns arranhões sérios de cabeça aqui até que eu percebesse que esse era o motivo.

if (isNaN(Number(value))) {
  alert('Number required.');
}

@pspi aprovado . Fazer _.isNumber() se comportar corretamente no sentido _semântico_ significa que eu nunca vou realmente usá-lo. ._isFinite() parece ser a função que funciona da maneira que eu esperava.

E, claro, um dia depois de postar isso, descobri que _.isFinite('1') é true , embora o argumento não seja um número.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

haggholm picture haggholm  ·  8Comentários

ksullivan picture ksullivan  ·  9Comentários

clouddueling picture clouddueling  ·  3Comentários

githublyp picture githublyp  ·  3Comentários

markvr picture markvr  ·  3Comentários