Pesquisa de site

javascript - Qual é a diferença entre `throw new Error` e `throw someObject`?


Eu quero escrever um manipulador de erros comum que irá capturar erros personalizados lançados de propósito em qualquer instância do código.

Quando eu fiz throw new Error('sample') como no código a seguir

try {
    throw new Error({'hehe':'haha'});
    // throw new Error('hehe');
} catch(e) {
    alert(e);
    console.log(e);
}

O log aparece no Firefox como Erro: [object Object] e eu não consegui analisar o objeto.

Para o segundo throw o log mostra como: Error: hehe

Considerando quando eu fiz

try {
    throw ({'hehe':'haha'});
} catch(e) {
    alert(e);
    console.log(e);
}

o console mostrou como: Object { hehe="haha"} no qual eu consegui acessar as propriedades do erro.

Qual é a diferença?

A diferença é como vista no código? Tipo, string será apenas passada como string e objeto como objetos, mas a sintaxe será diferente?

Eu não explorei lançar objetos de erro... Eu só tinha lançado strings.

Existe algum outro jeito além dos dois métodos mencionados acima?

Resposta #1

A diferença entre throw new Error(something) e throw something em JavaScript é que throw new Error(something) envolve o erro passado para ele no seguinte formato:

{name:'Error', message:something}

O throw something lançará a string/objeto como está.

Um benefício de throw new Error(something) é que, quando você console.log o objeto de erro capturado, você obterá um rastreamento de pilha, assim:

Error: <something>
    at foo (<anonymous>:3:11)
    at <anonymous>:9:1

Ambas as abordagens não permitirão nenhuma execução adicional de código a partir do bloco try.

Aqui está uma boa explicação sobre o objeto Error e como lançar seus próprios erros

O Objeto Erro

O que exatamente podemos extrair dele em caso de erro? O objeto Error em todos os navegadores suporta as seguintes duas propriedades:

  • nome: O nome do erro, ou mais especificamente, o nome da função construtora à qual o erro pertence.

  • mensagem: Uma descrição do erro, com essa descrição variando dependendo do navegador.

Seis valores possíveis podem ser retornados pela propriedade name, que, como mencionado, correspondem aos nomes dos construtores do erro. Eles são:

Error Name          Description

EvalError           An error in the eval() function has occurred.

RangeError          Out of range number value has occurred.

ReferenceError      An illegal reference has occurred.

SyntaxError         A syntax error within code inside the eval() function has occurred.
                    All other syntax errors are not caught by try/catch/finally, and will
                    trigger the default browser error message associated with the error. 
                    To catch actual syntax errors, you may use the onerror event.

TypeError           An error in the expected variable type has occurred.

URIError            An error when encoding or decoding the URI has occurred 
                   (ie: when calling encodeURI()).

Lançando seus próprios erros (exceções)

Em vez de esperar que um dos 6 tipos de erros ocorra antes que o controle seja automaticamente transferido do bloco try para o bloco catch, você também pode lançar explicitamente suas próprias exceções para forçar que isso aconteça sob demanda. Isso é ótimo para criar suas próprias definições do que é um erro e quando o controle deve ser transferido para o catch.

Resposta #2

lançar "Eu sou mau"

throw irá terminar a execução posterior e expor a string de mensagem ao capturar o erro.

try {
  throw "I'm Evil"
  console.log("You'll never reach to me", 123465)
} catch (e) {
  console.log(e); // I'm Evil
}

O console após throw nunca será alcançado devido à terminação.


throw new Error("Eu sou mau")

throw new Error expõe um evento de erro com dois parâmetros name e message. Também termina a execução subsequente

try {
  throw new Error("I'm Evil")
  console.log("You'll never reach to me", 123465)
} catch (e) {
  console.log(e.name, e.message); // Error I'm Evil
}

throw Error("Estou Maligno")

E apenas para completar, isso também funciona, embora tecnicamente não seja a maneira correta de fazê-lo -

try {
  throw Error("I'm Evil")
  console.log("You'll never reach to me", 123465)
} catch (e) {
  console.log(e.name, e.message); // Error I'm Evil
}

console.log(typeof(new Error("hello"))) // object
console.log(typeof(Error)) // function

Resposta #3

O artigo a seguir talvez entre em mais detalhes sobre qual é a melhor escolha; throw 'An error' ou throw new Error('An error'):

http://www.nczonline.net/blog/2009/03/10/the-art-of-throwing-javascript-errors-part-2/

Isso sugere que o último (new Error()) é mais confiável, já que navegadores como Internet Explorer e Safari (versões incertas) não reportam corretamente a mensagem ao usar o primeiro.

Fazer isso causará a ocorrência de um erro, mas nem todos os navegadores respondem da maneira que você esperaria. Firefox, Opera e Chrome exibem cada um uma mensagem de "exceção não capturada" e então incluem a string da mensagem. Safari e Internet Explorer simplesmente lançam um erro de "exceção não capturada" e não fornecem a string da mensagem. Claramente, isso é subótimo do ponto de vista de depuração.

Resposta #4

Resumo: eles são equivalentes Error(x) === new Error(x).

// this:
const x = Error('I was created using a function call!');
// has the same functionality as this:
const y = new Error('I was constructed via the "new" keyword!');

fonte: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Error

throw e throw Error são funcionalmente equivalentes. Mas quando você os captura e os serializa para console.log, eles não são serializados exatamente da mesma forma:

throw 'Parameter is not a number!';
throw new Error('Parameter is not a number!');
throw Error('Parameter is not a number!');

Console.log(e) do acima produzirá 2 resultados diferentes:

Parameter is not a number!
Error: Parameter is not a number!
Error: Parameter is not a number!

Resposta #5

Você menciona primeiro este código:

throw new Error('sample')

e então, no seu primeiro exemplo você escreve:

throw new Error({'hehe':'haha'}) 

O primeiro objeto Error seria realmente útil, porque ele está esperando um valor do tipo string, neste caso 'sample'. O segundo não seria, porque você está tentando passar um objeto, e ele está esperando uma string, e não exibiria um erro útil.

O objeto de erro teria a propriedade "message", que seria 'sample'.

Resposta #6

O construtor Error é usado para criar um objeto de erro. Objetos de erro são lançados quando ocorrem erros em tempo de execução. O objeto Error também pode ser usado como um objeto base para exceções definidas pelo usuário.

Erros definidos pelo usuário são lançados através da declaração throw. O controle do programa será passado para o primeiro bloco catch na pilha de chamadas.

A diferença entre lançar um erro com e sem o objeto Error:

throw {'hehe':'haha'};

No DevTools do Chrome, parece assim:

O Chrome nos informa que temos um erro não capturado, que é apenas um objeto JS. O próprio objeto poderia ter informações sobre o erro, mas ainda assim não sabemos imediatamente de onde ele veio. Não é muito útil quando estamos trabalhando no nosso código e depurando-o.

throw new Error({'hehe':'haha'}); 

No DevTools do Chrome, parece assim:

Um erro lançado com o objeto Error nos fornece um rastreamento de pilha quando o expandimos. Isso nos dá informações valiosas sobre de onde o erro exatamente veio, o que muitas vezes é uma informação útil ao depurar seu código. Observe ainda que o erro diz [object Object], isso ocorre porque o construtor Error espera uma string de mensagem como primeiro argumento. Quando ele recebe um objeto, ele o converterá em uma string.

Resposta #7

A classe Error inclui informações de depuração, como a pilha de chamadas do erro, como propriedades de suas instâncias. Os interpretadores de JS sabem como serializar instâncias de Error em uma string informativa de mensagem de erro, e sua estrutura também pode ser consumida por softwares de depuração - como as ferramentas de desenvolvimento do navegador - para construir uma representação de interface mais informativa do erro. É por isso que geralmente é mais útil lançar uma instância da classe Error em vez de simplesmente lançar, por exemplo, uma string descrevendo o erro, ou um número representando um código de erro.

Usando erros personalizados

É particularmente útil criar suas próprias subclasses de Error, que permitem identificar de forma única diferentes tipos de erro com um nome descritivo e legível por máquina...

  • pistas para depuração,
  • informações para mensagens de erro mais claras para o usuário, ou
  • informações para ajudar a se recuperar do erro.

Então, quando você estiver lidando com erros, você pode usar o operador instanceof de forma agradável e clara para verificar que tipo de erro ocorreu. Por exemplo:

class DangerousWaterCurrent extends Error {
    constructor(waterSpeed){
        super(`These waters are moving at ${waterSpeed} metres per second - too fast to cross!`) // Provide a `message` argument to the Error() constructor
        this.waterSpeed = waterSpeed // This passes some context about why/how the error occurred back to whichever function is going to catch & handle it
    }
}

// ...later...

try {
    swimAcrossRiver(river)
} catch (thrownValue) {
    if (thrownValue instanceof DangerousWaterCurrent) {
        if (thrownValue.waterSpeed <= 3){ 
            paddleKayak(river)
        } else {
            constructBridge(river)
        }
    } else {
        throw thrownValue // "Re-throw" the error back up the execution chain, for someone else to handle
    }
}

new Error() vs Error()

Existe uma forma abreviada "conveniente" de criar uma instância de Error: chamando Error(message), em vez de new Error(message), da maneira que você criaria uma instância de uma classe normal. Esta é uma exceção deliberada, inserida pelos designers da linguagem, à regra. Existem abreviações semelhantes para outras classes da própria linguagem, como Number() e String(). Elas também permitem que você chame essas classes com () como se fossem funções, e não classes. O JS não permite que classes normais façam isso, mesmo que todas elas sejam, na verdade, funções sob o açúcar sintático das "classes". Experimente em um REPL:

> class E extends Error {}
> Error(); "a value"
"a value"
> E(); "a value"
Uncaught TypeError: Class constructor E cannot be invoked without 'new'
    at <anonymous>:2:1

Uma opinião mais ampla sobre design: Pessoalmente, acho que essa decisão de design foi um erro, pois adiciona mais exceções às regras do JavaScript - o que significa mais para aprender para programadores e mais para tradutores/interpretadores de linguagem considerar. Em vez da nova palavra-chave do C++/Java, simplesmente chamar uma classe como se fosse uma função (como em Number("abc123")) deveria ter as propriedades que a nova palavra-chave atualmente possui: a função construtora da classe deve ser executada. Dentro dessa função, isso deve ser vinculado à instância e então retornado implicitamente. A nova palavra-chave podia então ser descartada do idioma. É assim que a sintaxe do Python funciona, e é mais simples, legível e conveniente.

Resposta #8

você pode throw como objeto

throw ({message: 'This Failed'})

então, por exemplo, no seu try/catch

try {
//
} catch(e) {
    console.log(e); //{message: 'This Failed'}
    console.log(e.message); //This Failed
}

ou simplesmente lançar um erro de string

throw ('Your error')

try {
//
} catch(e) {
    console.log(e); //Your error
}

throw new Error //only accept a string

Resposta #9

RESUMO

throw new Error('problem') captura várias propriedades do local onde o erro aconteceu.

throw 'problem' não

new Error('message') captura a pilha de execução + outros

Usar um objeto Error permite capturar a pilha de execução no ponto em que você lança o erro. Então, quando o erro é passado pela árvore de tratamento de erros, essa captura da pilha também é passada.

Então, inserir throw "test error" em algum lugar do meu código resulta em:

Enquanto throw new Error('test error') resulta em:

Você pode ver que o objeto Error nativo captura a pilha no ponto em que lanço o erro e a torna disponível para quem capturar o erro. Isso torna mais fácil para mim rastrear o problema quando estou depurando.

Além disso, ele também captura propriedades como fileName, lineNumber e columnNumber.

Se você usar o rastreamento de pilha, ele está lá para os rastreadores de exceção registrarem para você

Neste caso, a pilha está sendo impressa no console do navegador, mas se você estiver usando ferramentas de registro de erros em Javascript como Appsignal ou Bugsnag, essa pilha também estará disponível nelas. Se você inspecionar o objeto de erro, poderá acessar diretamente o instantâneo da pilha:

err = new Error('test')
err.stack

A heurística que uso para decidir qual formato usar

Quando não planejo capturar a exceção, eu uso new Error('problem')

Quando estou lançando um erro porque algo inesperado ou fora dos limites aconteceu na aplicação, digamos que o datastore local está corrompido, eu posso estar em uma situação em que não quero lidar com isso, mas quero sinalizá-lo. Nesse caso, vou usar o objeto Error para ter essa captura de stack.

Usando throw new Error('Datastore is corrupted') é mais fácil rastrear até o que aconteceu.

Quando eu planejo capturar a exceção eu uso throw 'problem'

Editar - ao reler isso, acho que a próxima parte precisa de um pouco de cautela. Geralmente, é uma boa ideia ser muito específico sobre qual erro você escolhe capturar, caso contrário, você pode acabar capturando coisas que realmente queria que subissem até o topo. Em geral, provavelmente é melhor criar um tipo de erro específico e capturar esse erro específico (ou a string da mensagem). Isso permite que erros que você não antecipou subam para a superfície.

Se o erro é um erro esperado que eu planejo capturar e tratar, então não vou obter muito proveito do snapshot da pilha.

Então, digamos que eu use um serviço http e ele retorna um código HTTP 500. Eu posso tratar isso como um erro que eu lanço "responseCode=500" e então, subsequentemente, capturo e trato.

Artigos relacionados