É melhor retornar ʻundefined` ou `null` de uma função javascript?

98

Tenho uma função que escrevi que basicamente se parece com isto:

function getNextCard(searchTerms) {
  // Setup Some Variables

  // Do a bunch of logic to pick the next card based on termed passed through what I'll call here as 'searchTerms' all of this logic is omitted because it's not important for my question.
  // ...

  // If we find a next card to give, than give it
  if (nextCardFound)
    return nextCardFound;

  // Otherwise - I'm returning undefined
  return undefined;
}

Pergunta: Seria melhor retornar "nulo" aqui?

Posso devolver o que quiser - obviamente ... Só não tinha certeza de qual é a melhor coisa a usar.

O código que chama esta função sabe como lidar com indefinidos (na verdade, nunca realmente acontecerá, a menos que algo dê terrivelmente errado)

Estou fazendo essa pergunta porque ouvi em algum lugar algo que soou como "Não atribua variáveis ​​indefinidas a variáveis" ou algo assim - que tornará mais difícil depurar. Portanto, o fato de que eu posso ver que nullé passado de volta me diz que o retorno está funcionando - mas basicamente funciona de forma semelhante a undefined.


Documentação:

Mozilla Docs não respondeu minha pergunta ... o google também não: \

Esta pergunta SO - era muito ampla para o que estou tentando descobrir aqui.

Jeremy Iglehart
fonte
1
esta pergunta SO não responde?
warkentien2
8
Na minha opinião, volte null. Deixe undefinedpara o próprio JavaScript. No entanto, não há "melhor", então isso é uma questão de opinião.
Felix Kling
@ warkentien2 Obrigado, isso foi útil - mas ainda não estou certo sobre qual é a convenção aqui para retornar de uma função getter.
Jeremy Iglehart
1
Eu li nullcomo "não há valor apropriado para o que você está pedindo" e undefinedcomo "Não consigo descobrir o que você está pedindo".
Marty
@ warkentien2 essa pergunta, e a que vinculei em minha resposta, estão relacionadas, mas ambas parecem estar se perguntando qual é a diferença entre elas e não quando usar uma ou outra como valor de retorno.
chiliNUT

Respostas:

37

Eu argumentarei que não existe a melhor maneira, e mesmo as funções padrão às vezes escolhem um ou outro.

Por exemplo:

  • [[Protótipo]]

    Objetos comuns têm um slot interno [[Prototype]], que determina de qual outro objeto eles herdam. Claro, deve haver uma maneira de dizer que um objeto não herda de nenhum outro. Nesse caso, "esse objeto não existe" é representado por null.

  • Object.getOwnPropertyDescriptor

    Espera-se retornar um descritor de propriedade, ou seja, um objeto que descreve uma propriedade (por exemplo, valor, capacidade de escrita, enumerabilidade e configurabilidade). No entanto, a propriedade pode não existir. Nesse caso, "essa propriedade não existe" é representado por undefined.

  • document.getElementById

    Espera-se que ele retorne o elemento com o ID fornecido. No entanto, pode não haver nenhum elemento com esse ID. Nesse caso, "esse elemento não existe" é representado por null.

Portanto, escolha o que você preferir ou achar que faz mais sentido para o seu caso específico.

Oriol
fonte
3
depois de ler isso , decidi sugerir a void 0técnica para futuros leitores desta resposta. Eu também adicionei alguns códigos para tentar deixar seu ponto mais claro. Obrigado pela sua resposta!
Jeremy Iglehart
114

Indefinido normalmente se refere a algo ao qual ainda não foi atribuído um valor (ainda). Nulo se refere a algo que definitivamente não tem valor. Nesse caso, eu recomendaria retornar um valor nulo. Observe que uma função sem valor de retorno especificado retorna implicitamente indefinido.

Da especificação ECMAScript2015

4.3.10 valor indefinido

valor primitivo usado quando uma variável não recebeu um valor

4.3.12 valor nulo

valor primitivo que representa a ausência intencional de qualquer valor de objeto

http://www.ecma-international.org/ecma-262/6.0/#sec-terms-and-definitions-undefined-type

Leitura adicional:

Quando é nulo ou indefinido usado em JavaScript?

chiliNUT
fonte
1
Sim, undefined é o valor usado quando uma variável não recebeu um valor. Por que exatamente isso implica que você não deve retornar undefined em uma função?
Oriol
1
@Oriol, na minha opinião, uma vez que uma função void retorna undefined, que é um valor reservado para funções desse tipo, de forma que ao lidar com o valor de retorno de uma função, null me diz que decidiu retornar null, enquanto undefined me diz ou decidiu devolver indefinido, ou decidiu não devolver nada, mas não sei com certeza qual. Além disso, se estou fazendo isso var x=someFunc();, estou atribuindo intencionalmente um valor xa e preferiria que ele não passasse em nenhum teste que indique que não foi (ou pode não ter) sido atribuído um valor. Just imho
chiliNUT
Esta deve ser a resposta aceita. É assim que ele deveria ser usado na especificação
Zinco
1
Eu não leio dessa forma. Eu li como: Se você definir uma variável, mas não a inicializar, ela terá um valor inicial de indefinido. Nulo deve ser usado pelo programador para indicar intencionalmente que uma variável está vazia. IMHO undefined nunca deve ser atribuído a uma variável pelo programador, deixe para o mecanismo js usá-lo. O valor do termo "objeto" é enganoso, pois em JS até mesmo os primitivos se comportam como objetos em sua maioria devido ao autoboxing
chiliNUT
1
Sim, isso faz sentido. Para ser justo, não me importo de usar um em vez do outro (embora esteja mais acostumado null), desde que você fique com um, mas ter 2 valores para indicar ausência de valor (seja qual for o "tipo") é sempre confuso
Sergio Rosas
39

Darei a você minha maneira opinativa de escolher entre os dois.

Minha pergunta simples é: o valor, dado outra entrada / estado / contexto, pode ser definido para algo?

Se a resposta for sim, use nullelse use undefined. Mais geralmente, qualquer função que retorna um objeto deve retornar nullquando o objeto pretendido não existe. Porque poderia existir dado outra entrada / estado / contexto.

nullrepresenta a ausência de valor para uma determinada entrada / estado / contexto. Significa implicitamente que o conceito do próprio valor existe no contexto de seu aplicativo, mas pode estar ausente. Em seu exemplo, o conceito de uma próxima carta existe, mas a própria carta pode não existir. nulldeve ser usado.

undefinedrepresenta implicitamente a ausência de significado desse valor no contexto do seu aplicativo. Por exemplo, se eu manipular um userobjeto com um determinado conjunto de propriedades e tentar acessar a propriedade pikatchu. O valor dessa propriedade deve ser definido como undefinedporque, em meu contexto, não faz sentido ter tal propriedade.

Ngryman
fonte
1
Isso soa tão verdadeiro para mim. As funções puras da IMO devem retornar null, enquanto as funções com efeitos colaterais devem retornar undefined, ao pensar como um programador funcional.
Jake
4

undefinednão é algo que você deva atribuir. Você pode querer retornar algo diferente de undefined. No seu caso, mesmo que você não retorne nada, o resultado undefinedjá será . Então, eu sugiro ir com null.

Considere esta amostra,

function getSomething() {
     // .. do something
     return undefined;
}

function doSomething() {
     // .. I'm not gonna return anything.
}

var a = getSomething();
var b = doSomething();

Resultado da amostra acima em a === b, que é undefined. A diferença é que você salva 1 execução de instrução.

choz
fonte
@Oriol, quero dizer, undefinednão precisa ser atribuído. Todas as variáveis ​​declaradas sem valores já são undefined.
choz
@choz & @Oriol - como mencionado anteriormente por @chiliNUT "Observe que uma função sem valor de retorno especificado retorna implicitamente indefinido." - isso é verdade porque (function(){ /* code */ })()retorna null em um console.
Jeremy Iglehart
@JeremyIglehart Esse código na verdade não retorna nada. E mais, dá undefinedno meu console Chrome e Firefox.
choz
OK, não entendi seu ponto. Sim, se você não retornar nada explicitamente, undefined será retornado implicitamente. Mas por que isso importa?
Oriol
1
@Oriol, acho que @choz estava tentando dizer (como alguns outros também mencionados nesta questão) que se eu quiser retornar undefinedse algo não retornar antes - eu não preciso porque o comportamento padrão da função se você não retorna nada é retornar indefinido - eles estão apenas dizendo que isso não é necessário. Além disso ... gostei do que você disse sobre as funções getter integradas que retornam null. Por favor, poste sua resposta para esse efeito e eu a aceitarei.
Jeremy Iglehart
3

Depende do que você precisa fazer com o valor retornado.

typeof null retorna um objeto. esse objeto tem um valor de indefinido

typeof undefined retorna undefined

Dan
fonte
Pessoalmente, costumo usar null.
Dan
4
"aquele objeto tem um valor indefinido" Não, não é e não é um objeto, é Nulo. typeofnão retorna necessariamente o tipo de dados verdadeiro de um valor, ele tem um mapa que mapeia os tipos de dados para rótulos e retorna o rótulo correspondente.
Felix Kling
Não confie typeof, apesar do nome não diz o tipo de um valor.
Oriol
2

Aqui está um exemplo onde undefinedfaz mais sentido do que null:

Eu uso uma função de wrapper para JSON.parseconverter sua exceção emundefined :

// parses s as JSON if possible and returns undefined otherwise
// return undefined iff s is not a string or not parseable as JSON; undefined is not a valid JSON value https://stackoverflow.com/a/14946821/524504
function JSON_parse_or_undefined(s) {
    if ("string" !== typeof s) return undefined

    try {
        const p = JSON.parse(s)
        return p
    } catch (x){}

    return undefined
}

Observe que nullé válido em JSON, enquanto undefinednão é.

masterxilo
fonte
Vejo o que você está fazendo lá - e não posso dizer que esteja errado - porque, de certa forma, acho que você poderia fazer isso aqui e seria ótimo. Eu tenho um padrão diferente que uso para fazer esta operação que eu gosto mais porque eu faço uma etapa de "validação" depois. Eu sinto que isso está misturando validação com o retorno do valor. Aqui está o que eu faço: let getStringOrJSON = value => { try { value = JSON.parse(value); } catch(e) { return value; } return value; };. Agora, tenho certeza de que as duas devoluções podem ser tratadas de forma diferente e podem não vencer a competição de golfe JS. Funciona.
Jeremy Iglehart
1

A primeira resposta está certa. Eles têm significados teoricamente diferentes. No entanto, nem sempre está claro qual escolher.

Eu tendo a usar null no meu desenvolvimento, embora eu ache que isso seja uma coisa completamente subjetiva.

Eu uso isso principalmente porque:

  1. variável indefinida pode ser substituída em navegadores antigos, portanto, devolvê-la é um pouco mais complicado. Esse mesmo problema o força a usar typeof var === 'undefined'ao obter resultados de funções. ligação

  2. Outras linguagens tendem a usar null amplamente, muitas delas nem mesmo têm undefined (php por exemplo). Isso me dá um tipo de consistência ao alternar rapidamente entre os idiomas.

Maciej Paprocki
fonte
1

Eu acho que é muito discutível o que usar. Eu prefiro um código que seja semanticamente o mais preciso possível, então acho que undefinedé apropriado neste caso.

Eu penso em nullatribuições como significando "uma variável definida para nada". Isso é o oposto deundefined significar "essa coisa não está lá"

Como uma resposta anterior apontou, retornar undefinedtem problemas e você decide se isso o incomoda. Isso não me incomodaria.

Ryan Laboucane
fonte
2
Mas document.getElementById('iDoNotExist')retorna null, mesmo que o significado esteja mais próximo de "essa coisa não está lá de jeito nenhum". Se os métodos padrão fazem isso, por que não o OP?
Oriol
@Oriol Na verdade, gosto mais do seu raciocínio. Por favor, poste uma resposta para esse efeito e eu aceitarei. (Posso até adicionar algumas edições, se necessário)
Jeremy Iglehart
Sim, @Oriol, é por isso que gosto de debates, mesmo em sites de perguntas e respostas. É muito bom obter contra-exemplos. E você forneceu um bom.
Ryan Laboucane
1

Eu diria que, neste caso, nulldeve ser devolvido.

Se você considerar a questão de um ponto de vista teórico da ciência da computação, undefined é usado para indicar não terminação / não computabilidade (isto é, o espaço reservado para um ponto indefinido xde uma função parcial f que é freqüentemente escrita f(x) = ⊥).

getNextCardno entanto, parece ser capaz de calcular a próxima carta (se houver) e também ser capaz de calcular se não houver uma próxima carta. Em outras palavras, a função é total, pois termina para cada entrada.

Dito isto, é necessária uma terminação de sinalização de valor especial sem resultado significativo (ou seja, "não há cartão que eu possa retornar para esta entrada") e isso para mim nullnão é undefined.


NOTAS:

Você pode ver algum suporte para esse argumento em algumas outras linguagens tipadas, bem como onde a terminação sem resultado significativo é expressa usando um tipo de opção (às vezes também referido como tipo anulável ). Um exemplo disso é Maybe in Haskell .

Por outro lado, é claro que não sabemos o que undefinedem JavaScript realmente significa. Então, a analogia com indefinido é um pouco tênue. Além disso, como sempre queremos trabalhar com funções totais, isso equivale a dizer "nunca retorne undefinedde uma função". O que parece ser um pouco estrito, pois limitaria o uso de undefinedpropriedades / variáveis ​​que não foram definidas.

No final, minha preferência pessoal é nunca mais voltar undefinedaonde eu possa voltar nulle eu também argumentaria que essa é a melhor convenção de codificação (porque, entre outras coisas, x !== nullé mais curta do que typeof x !== 'undefined').

FK82
fonte
-1

Minha opinião pessoal de acordo com minha experiência é não use undefined e null se você não quiser travar seu código. Pelo menos eu o evitaria pessoalmente. Existem muitas funções em Javascript que retornam indefinidas e ok devemos usar para isso. Mas quando você projetar seu código, não o use. É importante sempre devolver algo, "false"pelo menos. Se você tem um array, por exemplo, e mapeia sobre ele. Não é bom voltar [undefined, undefined.....]nem justo undefined. É melhor se você mantiver o tipo do array original. Exemplo:

 const mapper:Map <string[],boolean[]>  
['i', 'dont', 'use', 'null or undefined'] -> [false, true, false, true, false]
or ['', dont, '', '', use] 
or al the stuff above and then filter(v => v)
that will keep all undefined and null out

Essa é a ideia. Eu tento o tempo todo evitar isso. Porque um nullou undefinedpode facilmente travar seu código

rubendmatos1985
fonte