JavaScript nos permite trabalhar com primitivos (strings, números, etc.) como se fossem objetos. Eles também fornecem métodos para chamar como tal. Estudaremos isso em breve, mas primeiro veremos como funciona porque, é claro, primitivos não são objetos (e aqui deixaremos isso ainda mais claro).
Vejamos as principais distinções entre primitivos e objetos.
Um primitivo
É um valor de um tipo primitivo.
Existem 7 tipos primitivos: string
, number
, bigint
, boolean
, symbol
, null
e undefined
.
Um objeto
É capaz de armazenar vários valores como propriedades.
Pode ser criado com {}
, por exemplo: {name: "John", age: 30}
. Existem outros tipos de objetos em JavaScript: funções, por exemplo, são objetos.
Uma das melhores coisas sobre objetos é que podemos armazenar uma função como uma de suas propriedades.
deixe João = { nome: "João", digaOi: function() { alerta("Olá amigo!"); } }; john.sayOi(); // Olá amigo!
Então aqui criamos um objeto john
com o método sayHi
.
Já existem muitos objetos embutidos, como aqueles que trabalham com datas, erros, elementos HTML, etc. Eles possuem propriedades e métodos diferentes.
Mas esses recursos têm um custo!
Os objetos são “mais pesados” que os primitivos. Eles exigem recursos adicionais para apoiar o maquinário interno.
Aqui está o paradoxo enfrentado pelo criador do JavaScript:
Há muitas coisas que alguém gostaria de fazer com um primitivo, como uma string ou um número. Seria ótimo acessá-los usando métodos.
Os primitivos devem ser tão rápidos e leves quanto possível.
A solução parece um pouco estranha, mas aqui está:
Os primitivos ainda são primitivos. Um único valor, conforme desejado.
A linguagem permite acesso a métodos e propriedades de strings, números, booleanos e símbolos.
Para que isso funcione, um “invólucro de objeto” especial que fornece funcionalidade extra é criado e depois destruído.
Os “invólucros de objetos” são diferentes para cada tipo primitivo e são chamados: String
, Number
, Boolean
, Symbol
e BigInt
. Assim, eles fornecem diferentes conjuntos de métodos.
Por exemplo, existe um método de string str.toUpperCase() que retorna um str
maiúsculo.
Veja como funciona:
deixe str = "Olá"; alerta(str.toUpperCase()); // OLÁ
Simples, certo? Aqui está o que realmente acontece em str.toUpperCase()
:
A string str
é uma primitiva. Assim, no momento de acessar sua propriedade, é criado um objeto especial que conhece o valor da string, e possui métodos úteis, como toUpperCase()
.
Esse método é executado e retorna uma nova string (mostrada por alert
).
O objeto especial é destruído, deixando o str
primitivo em paz.
Portanto, os primitivos podem fornecer métodos, mas ainda permanecem leves.
O mecanismo JavaScript otimiza altamente esse processo. Pode até pular a criação do objeto extra. Mas ainda deve aderir à especificação e comportar-se como se a criasse.
Um número possui métodos próprios, por exemplo, toFixed(n) arredonda o número para a precisão fornecida:
seja n = 1,23456; alerta( n.toFixed(2) ); //1.23
Veremos métodos mais específicos nos capítulos Números e Strings.
Construtores String/Number/Boolean
são apenas para uso interno
Algumas linguagens como Java nos permitem criar explicitamente “objetos wrapper” para primitivos usando uma sintaxe como new Number(1)
ou new Boolean(false)
.
Em JavaScript, isso também é possível por razões históricas, mas altamente não recomendado . As coisas vão enlouquecer em vários lugares.
Por exemplo:
alerta(tipo de 0); // "número" alerta(tipo de novo número(0)); // "objeto"!
Os objetos são sempre verdadeiros if
, então aqui o alerta aparecerá:
seja zero = novo número (0); if (zero) { // zero é verdadeiro, porque é um objeto alert("zero é verdade!?!" ); }
Por outro lado, usar as mesmas funções String/Number/Boolean
sem new
é algo totalmente bom e útil. Eles convertem um valor para o tipo correspondente: em uma string, um número ou um booleano (primitivo).
Por exemplo, isso é inteiramente válido:
deixe num = Número("123"); //converte uma string em número
nulo/indefinido não tem métodos
As primitivas especiais null
e undefined
são exceções. Eles não possuem “objetos wrapper” correspondentes e não fornecem métodos. Em certo sentido, eles são “os mais primitivos”.
Uma tentativa de acessar uma propriedade com esse valor daria o erro:
alerta(nulo.teste); //erro
Primitivos, exceto null
e undefined
fornecem muitos métodos úteis. Estudaremos isso nos próximos capítulos.
Formalmente, esses métodos funcionam por meio de objetos temporários, mas os mecanismos JavaScript são bem ajustados para otimizar isso internamente, portanto, sua chamada não é cara.
importância: 5
Considere o seguinte código:
deixe str = "Olá"; str.teste = 5; alerta(str.teste);
O que você acha, vai funcionar? O que será mostrado?
Tente executá-lo:
deixe str = "Olá"; str.teste = 5; // (*) alerta(str.teste);
Dependendo se você use strict
ou não, o resultado pode ser:
undefined
(sem modo estrito)
Um erro (modo estrito).
Por que? Vamos repetir o que está acontecendo na linha (*)
:
Quando uma propriedade de str
é acessada, um “objeto wrapper” é criado.
No modo estrito, escrever nele é um erro.
Caso contrário, a operação com a propriedade é continuada, o objeto obtém a propriedade test
, mas depois disso o “objeto wrapper” desaparece, então na última linha str
não tem nenhum vestígio da propriedade.
Este exemplo mostra claramente que os primitivos não são objetos.
Eles não podem armazenar dados adicionais.