Domine JavaScript: Perguntas e Respostas Frequentes em Entrevistas

Adicionar proficiência em JavaScript ao seu portfólio aumenta significativamente as chances de conseguir uma posição como desenvolvedor de software. Dito isso, vamos explorar as questões mais comuns que surgem em entrevistas sobre JavaScript.

JavaScript é uma das linguagens de programação mais utilizadas no desenvolvimento web, e hoje em dia, é empregada para construir praticamente qualquer tipo de aplicativo.

Antes de nos aprofundarmos nas perguntas de entrevista, vamos destacar algumas das vantagens de aprender JavaScript.

JavaScript é uma linguagem de programação leve, interpretada ou compilada just-in-time. Ela é um dos pilares fundamentais da World Wide Web. Caso você não esteja familiarizado com os outros dois idiomas essenciais da web, sugiro que os explore.

JavaScript foi originalmente projetado para a web, mas sua aplicabilidade se expandiu consideravelmente. Com o auxílio de ambientes como Node e Deno, podemos executá-lo em diversas plataformas.

Vamos examinar algumas de suas vantagens:

Vantagens do JavaScript

  • É fácil começar a aprender, mesmo sem experiência prévia em programação.
  • Possui uma grande comunidade de suporte, onde você pode encontrar ajuda sempre que precisar.
  • Existem muitas bibliotecas e frameworks construídos em JavaScript, que agilizam o desenvolvimento de aplicativos.
  • JavaScript permite o desenvolvimento de aplicações para frontend, backend, Android, iOS e muito mais, abrangendo quase todos os tipos de aplicativos, com destaque para o desenvolvimento web.

Quais são os tipos de dados em JavaScript?

Tipos de dados são usados para armazenar diferentes formas de informação. Eles variam de linguagem para linguagem. Em JavaScript, temos 8 tipos de dados principais. Vamos explorá-los individualmente:

  • Number (Número)
  • String (Corda)
  • Boolean (Booleano)
  • Undefined (Indefinido)
  • Null (Nulo)
  • BigInt
  • Symbol (Símbolo)
  • Object (Objeto)

Todos os tipos de dados, com exceção de Object, são considerados valores primitivos e são imutáveis.

Quais são os métodos internos em JavaScript?

Os métodos internos em JavaScript variam para cada tipo de dado. Eles são acessíveis utilizando o respectivo tipo de dado. Vamos ver alguns métodos integrados comuns para diferentes tipos de dados e estruturas:

  • Number
  • String
    • toLowerCase
    • startsWith
    • charAt
  • Array

Existem muitos outros métodos internos para cada tipo de dado. É recomendável verificar as referências para obter uma lista completa dos métodos disponíveis.

Como criar um array em JavaScript?

Arrays são uma estrutura de dados fundamental em JavaScript. Eles podem conter qualquer tipo de dado, devido à natureza dinâmica da linguagem. Vejamos como criar arrays:

Podemos criar um array usando colchetes []. É uma forma rápida e direta de criar arrays:

// Array vazio
const arr = [];

// Array com valores aleatórios
const randomArr = [1, "Um", true];

console.log(arr, randomArr);

Também podemos criar arrays usando o construtor Array, embora essa forma seja menos comum em projetos gerais:

// Array vazio
const arr = new Array();

// Array com valores aleatórios
const randomArr = new Array(1, "Um", true);

console.log(arr, randomArr);

Arrays em JavaScript são mutáveis, permitindo que sejam modificados após a sua criação.

Como criar um objeto em JavaScript?

Objetos, assim como arrays, são estruturas de dados centrais em JavaScript, utilizados para armazenar pares chave-valor. A chave deve ser um valor imutável, enquanto o valor pode ser de qualquer tipo. Veja como criar objetos:

Podemos usar chaves {} para criar objetos. É um método direto e rápido:

// Objeto vazio
const object = {};

// Objeto com valores aleatórios
const randomObject = { 1: 2, um: "Dois", true: false };

console.log(object, randomObject);

Outra forma é usar o construtor Object, que é menos comum em projetos:

// Objeto vazio
const object = new Object();

// Objeto com valores aleatórios
const randomObject = new Object();
randomObject[1] = 2;
randomObject["um"] = "Dois";
randomObject[true] = false;

console.log(object, randomObject);

Objetos JavaScript são mutáveis, permitindo alterações após a criação, como demonstrado no segundo exemplo.

Como depurar código JavaScript?

A depuração de código pode ser desafiadora, variando de acordo com a linguagem, projeto e outros fatores. Vamos explorar métodos comuns para depurar JavaScript:

1. Registro

Podemos usar `console.log` em várias partes do código para identificar erros. A execução do código é interrompida em caso de erro em uma linha anterior.

O registro é um método de depuração tradicional, eficaz para pequenos projetos, e comum em várias linguagens.

2. Ferramentas de desenvolvedor

JavaScript é amplamente utilizado em aplicações web, portanto, a maioria dos navegadores possui ferramentas de desenvolvedor que auxiliam na depuração do código.

A definição de breakpoints nas ferramentas do desenvolvedor é um dos métodos mais usados. Breakpoints interrompem a execução do código, fornecendo informações detalhadas sobre o estado do programa naquele momento.

Podemos definir vários breakpoints em torno da área com erros e investigar suas causas, o que é particularmente útil na depuração de aplicações web JavaScript.

3. IDEs

IDEs também são ferramentas eficazes para depuração de JavaScript. O VS Code, por exemplo, oferece suporte à depuração com breakpoints. A funcionalidade de depuração pode variar entre as IDEs, mas a maioria oferece essa capacidade.

Como adicionar código JavaScript em um arquivo HTML?

Código JavaScript pode ser adicionado a um arquivo HTML através da tag script. Veja o exemplo a seguir:

<html lang="pt">
  <head>
    <title>etechpt.com</title>
  </head>
  <body>
    <h2>etechpt.com</h2>

    <script>
      // Código JavaScript aqui
      console.log("Este é um código JavaScript");
    </script>
  </body>
</html>

O que são cookies?

Cookies são pares chave-valor usados para armazenar pequenas informações sobre um usuário. Eles têm um tempo de expiração, sendo deletados após esse período. São amplamente utilizados para armazenar informações de usuários.

Cookies não são apagados ao atualizar a página, persistindo até sua exclusão ou expiração. Eles podem ser inspecionados através das ferramentas de desenvolvedor.

Como ler um cookie?

Cookies podem ser lidos em JavaScript através de `document.cookie`, que retorna todos os cookies criados.

console.log("Todos os cookies:", document.cookie);

Se nenhum cookie estiver presente, uma string vazia será retornada.

Como criar e excluir um cookie?

Cookies podem ser criados definindo um par chave-valor para `document.cookie`. Veja o exemplo:

document.cookie = "um=Um";

Além do par chave-valor, podemos adicionar atributos como domínio, caminho e tempo de expiração. Esses atributos devem ser separados por ponto e vírgula (;), sendo todos opcionais.

Um exemplo com atributos:

document.cookie = "um=Um; expires=Jan 31 2023; path=/;";

Neste código, adicionamos uma data de expiração e um caminho para o cookie. Se a data de expiração não for especificada, o cookie será excluído ao final da sessão. O caminho padrão é o caminho do arquivo, e a data de expiração deve estar no formato GMT.

Para criar vários cookies:

document.cookie = "um=Um;expires=Jan 31 2023;path=/;";
document.cookie = "dois=Dois;expires=Jan 31 2023;path=/;";
document.cookie = "tres=Tres;expires=Jan 31 2023;path=/;";

Cookies não são substituídos se a chave ou o caminho forem diferentes. Caso contrário, um cookie existente será substituído. O exemplo abaixo substitui o cookie definido anteriormente:

document.cookie = "um=Um;expires=Jan 31 2023;path=/;";
document.cookie = "um=Dois;path=/;";

Neste caso, a data de expiração foi removida e o valor do cookie foi alterado.

Ao testar o código, use uma data futura para a expiração, para assegurar que os cookies sejam criados. Se você usar a data de 31 de janeiro de 2023 após essa data, os cookies não serão criados.

Para excluir cookies, basta alterar a data de expiração para uma data no passado. Veja o exemplo:

// Criando cookies
document.cookie = "um=Um;expires=Jan 31 2023;path=/;";
document.cookie = "dois=Dois;expires=Jan 31 2023;path=/;";
document.cookie = "tres=Tres;expires=Jan 31 2023;path=/;";

// Excluindo o último cookie
document.cookie = "tres=Tres;expires=Jan 1 2023;path=/;";

O último cookie não estará mais disponível, pois foi excluído na última linha do código.

Quais são os diferentes frameworks JavaScript?

Existe uma grande variedade de frameworks JavaScript disponíveis. Para desenvolvimento de interface do usuário, temos React, Vue e Angular. Para o desenvolvimento do lado do servidor, Express, Koa e Nest são opções populares. Para geração de sites estáticos, NextJS e Gatsby são utilizados. Já para desenvolvimento de aplicativos móveis, podemos citar React Native e Ionic, entre outros. A lista é vasta e a exploração de cada um exigiria bastante tempo.

Closures em JavaScript

Um closure é uma função que encapsula seu escopo léxico e o ambiente léxico de seu pai. Eles permitem acessar dados de um escopo externo e são formados durante a criação de funções.

function externa() {
  const a = 1;
  function interna() {
    // Podemos acessar dados do escopo da função externa aqui
    // Os dados estão acessíveis mesmo executando esta função fora da função externa
    // devido ao closure formado durante sua criação
    console.log("Acessando a dentro de interna", a);
  }
  return interna;
}

const funcaoInterna = externa();
funcaoInterna();

Closures são amplamente utilizados em aplicações JavaScript. É possível que você já os tenha usado sem perceber. É importante ter um bom entendimento desse conceito.

Hoisting em JavaScript

Hoisting é um processo em JavaScript onde as declarações de variáveis, funções e classes são movidas para o topo do escopo antes da execução do código.

// Acessando `nome` antes de declarar
console.log(nome);

// Declarando e inicializando `nome`
var nome = "etechpt.com";

O código acima não gerará erros, mas em muitas linguagens, causaria um erro. A saída será `undefined`, já que o hoisting move apenas as declarações para o topo, sem inicializá-las.

Agora, vamos alterar o `var` para `let` ou `const` e executar o código novamente:

// Acessando `nome` antes de declarar
console.log(nome);

// Declarando e inicializando `nome`
const nome = "etechpt.com";

Neste caso, receberemos um `ReferenceError` informando que não é possível acessar a variável antes da inicialização.

ReferenceError: Cannot access 'nome' before initialization

`let` e `const`, introduzidos no ES6, não permitem acesso à variável antes da inicialização. Variáveis declaradas com `let` ou `const` permanecem na Temporal Dead Zone (TDZ) até que sejam inicializadas, impedindo seu acesso.

Currying em JavaScript

Currying é uma técnica que converte funções com vários parâmetros em funções com um único parâmetro, através de múltiplas chamadas. Ele transforma `add(a, b, c, d)` em `add(a)(b)(c)(d)`. Veja um exemplo:

function obterFuncaoCurry(callback) {
  return function (a) {
    return function (b) {
      return function (c) {
        return function (d) {
          return callback(a, b, c, d);
        };
      };
    };
  };
}

function add(a, b, c, d) {
  return a + b + c + d;
}

const addCurry = obterFuncaoCurry(add);

// Chamando addCurry
console.log(addCurry(1)(2)(3)(4));

A função `obterFuncaoCurry` pode ser generalizada para ser usada com diferentes funções para transformá-las em funções com `currying`. É importante aprofundar-se na documentação de JavaScript para obter mais detalhes sobre este conceito.

Diferença entre `document` e `window`

O objeto `window` é o objeto de nível mais alto no navegador, que contém informações sobre a janela, como histórico, localização e navegador. Ele está globalmente disponível em JavaScript, podendo ser usado diretamente em nosso código, sem a necessidade de importações. Suas propriedades e métodos podem ser acessados sem mencionar o `window`.
O objeto `document` é parte do objeto `window`. O HTML carregado na página web é convertido no objeto `document`, que se refere a um elemento HTMLDocument com propriedades e métodos específicos para elementos HTML. O objeto `window` representa a janela do navegador, enquanto `document` representa o documento HTML carregado nessa janela.

Diferença entre lado do cliente e lado do servidor

Lado do cliente se refere ao usuário final que interage com a aplicação. Lado do servidor se refere ao servidor web onde a aplicação está hospedada.

Na terminologia frontend, podemos dizer que o navegador nos computadores dos usuários representa o lado do cliente, e serviços em nuvem representam o lado do servidor.

Diferença entre `innerHTML` e `innerText`

Tanto `innerHTML` quanto `innerText` são propriedades de elementos HTML, que permitem modificar o conteúdo de elementos HTML.

`innerHTML` interpreta o HTML atribuído a ele, renderizando-o como um HTML normal. Veja o exemplo:

const titleEl = document.getElementById("titulo");

titleEl.innerHTML = '<span style="color:orange;">etechpt.com</span>';

Ao adicionar um elemento com o `id` de `titulo` no seu HTML e executar o código JavaScript, você verá `etechpt.com` na cor laranja. Ao inspecionar o elemento, verá que ele está dentro de uma tag `span`. `innerHTML` interpretará o HTML e o renderizará normalmente.

Por outro lado, `innerText` tratará uma string como texto puro e a renderizará exatamente como fornecida, sem interpretar HTML. Altere `innerHTML` para `innerText` no código acima e observe a saída:

const titleEl = document.getElementById("titulo");

titleEl.innerText="<span style="color:orange;">etechpt.com</span>";

Neste caso, a string fornecida será exibida diretamente na página web.

Diferença entre `let` e `var`

`let` e `var` são palavras-chave utilizadas para criar variáveis em JavaScript, sendo `let` uma adição do ES6.

`let` tem escopo de bloco, enquanto `var` tem escopo de função.

{
  let a = 2;
  console.log("Dentro do bloco", a);
}
console.log("Fora do bloco", a);

O código acima causará um erro na última linha, pois `a` foi declarada com `let`, e não pode ser acessada fora do bloco. Substituindo `let` por `var`:

{
  var a = 2;
  console.log("Dentro do bloco", a);
}
console.log("Fora do bloco", a);

Neste caso, não haverá erros, pois `var` também pode ser acessado fora do bloco. Agora, vamos substituir o bloco por uma função:

function exemplo() {
  var a = 2;
  console.log("Dentro da função", a);
}
exemplo();
console.log("Fora da função", a);

Neste caso, haverá um erro de referência, pois `var` tem escopo de função, e não pode ser acessado fora da função.

Com `var`, podemos redeclarar variáveis, o que não é possível com `let`. Veja o exemplo:

var a = "etechpt.com";
var a = "Chandan";
console.log(a);
let a = "etechpt.com";
let a = "Chandan";
console.log(a);

O primeiro código não causará erros, alterando o valor de `a` para o último valor atribuído. O segundo código resultará em um erro, pois `let` não permite redeclaração de variáveis.

Diferença entre `sessionStorage` e `localStorage`

Tanto `sessionStorage` quanto `localStorage` são usados para armazenar informações nos computadores dos usuários, que podem ser acessadas sem internet. Eles armazenam pares chave-valor, que são convertidos para strings se fornecidos com outro tipo de dado.
`sessionStorage` é limpo ao fim da sessão (quando o navegador é fechado), enquanto `localStorage` não é limpo, a menos que seja explicitamente removido.

Podemos acessar, atualizar e excluir dados armazenados no `sessionStorage` e `localStorage` através dos respectivos objetos.

O que é NaN em JavaScript?

NaN é abreviação de Not-a-Number (não é um número), indicando que algo não é um número válido em JavaScript. Casos como `0/0`, `undefined * 2`, `1 + undefined` e `null * undefined` resultam em NaN.

O que é escopo léxico?

Escopo léxico se refere ao acesso a variáveis de escopo pai. Uma função com funções internas pode acessar as variáveis de suas funções pai. A função mais interna pode acessar as variáveis das duas funções pai. Veja um exemplo:

function maisExterna() {
  let a = 1;
  console.log(a);
  function meio() {
    let b = 2;
    // `a` é acessível aqui
    console.log(a, b);
    function maisInterna() {
      let c = 3;
      // `a` e `b` são acessíveis aqui
      console.log(a, b, c);
    }
    maisInterna();
  }
  meio();
}
maisExterna();

JavaScript usa uma cadeia de escopo para encontrar uma variável quando acessada. Ele primeiro verifica o escopo atual, depois o escopo pai e assim por diante, até o escopo global.

O que é passagem por valor e passagem por referência?

Passagem por valor e passagem por referência são duas formas de passar argumentos para uma função em JavaScript.

Na passagem por valor, uma cópia dos dados originais é passada para a função, de forma que alterações feitas dentro da função não afetam os dados originais. Veja o exemplo:

function exemplo(a) {
  // Alterando o valor de `a`
  a = 5;
  console.log("Dentro da função", a);
}
let a = 3;
exemplo(a);
console.log("Fora da função", a);

O valor original de `a` não é alterado, mesmo tendo sido modificado dentro da função.

Na passagem por referência, uma referência aos dados é passada para a função, de modo que alterações feitas dentro da função também alteram os dados originais.

function exemplo(arr) {
  // Adicionando um novo valor ao array
  arr.push(3);
  console.log("Dentro da função", arr);
}
let arr = [1, 2];
exemplo(arr);
console.log("Fora da função", arr);

O valor original de `arr` é modificado quando alterado dentro da função.

Tipos de dados primitivos são passados por valor e não primitivos por referência.

O que é memoização?

Memoização é uma técnica que armazena valores calculados em cache, reutilizando-os quando necessário, sem recálculo. Isso acelera a execução do código em casos de computações pesadas. O custo de armazenamento é compensado pelo ganho de tempo.

const memo = {};
function add(a, b) {
  const key = `${a}-${b}`;

  // Verificando se o valor já foi calculado
  if (memo[key]) {
    console.log("Não calculando novamente");
    return memo[key];
  }

  // Adicionando novo valor calculado no cache
  // Aqui o cache é um objeto global simples
  memo[key] = a + b;
  return memo[key];
}

console.log(add(1, 2));
console.log(add(2, 3));
console.log(add(1, 2));

O exemplo acima demonstra memoização. A soma de dois números não é uma operação pesada, mas serve como ilustração do conceito.

Qual é o parâmetro rest?

O parâmetro rest é usado para coletar todos os parâmetros restantes de uma função. Em uma função que aceita um mínimo de dois argumentos e um número máximo indefinido de argumentos, podemos coletar os dois primeiros parâmetros com variáveis normais e os demais com o parâmetro rest, usando o operador rest.

function exemplo(a, b, ...rest) {
  console.log("Parâmetro rest", rest);
}

exemplo(1, 2, 3, 4, 5);

O parâmetro rest será um array dos últimos três argumentos do exemplo. Ele permite ter um número indefinido de parâmetros em uma função. Uma função pode ter apenas um parâmetro rest, e este deve ser o último na ordem dos parâmetros.

O que é desestruturação de objetos?

A desestruturação de objetos é usada para acessar variáveis de um objeto e atribuí-las a variáveis com os mesmos nomes das chaves do objeto. Veja o exemplo:

const object = { a: 1, b: 2, c: 3 };

// Desestruturação do objeto
const { a, b, c } = object;

// Agora, a, b, c podem ser usados como variáveis normais
console.log(a, b, c);

Podemos alterar os nomes das variáveis desestruturadas na mesma linha, da seguinte forma:

const object = { a: 1, b: 2, c: 3 };

// Alterando os nomes de `a` e `b`
const { a: changedA, b: changedB, c } = object;

// Agora, changedA, changedB, c podem ser usados como variáveis normais
console.log(changedA, changedB, c);

O que é desestruturação de array?

A desestruturação de arrays permite acessar variáveis de um array e atribuí-las a variáveis. Veja o exemplo:

const array = [1, 2, 3];

// Desestruturação do array
// Baseada no índice do array
const [a, b, c] = array;

// Agora, a, b, c podem ser usados como variáveis normais
console.log(a, b, c);

O que são captura de eventos e bubbling de eventos?

Captura de eventos e bubbling de eventos são duas formas de propagação de eventos no HTML DOM. Imagine dois elementos HTML, um dentro do outro. Se um evento ocorrer no elemento interno, o modo de propagação do evento determinará a ordem de execução dos eventos.

Bubbling de eventos executa o manipulador de eventos primeiro no elemento onde o evento ocorreu, e depois em seus elementos pai, até chegar ao elemento mais alto. Este é o comportamento padrão de todos os eventos.

Na captura de eventos, precisamos especificar que queremos usar este tipo de propagação. Ao adicionar o listener do evento, é possível indicar que a propagação será por captura de evento. Neste caso, os eventos serão executados na seguinte ordem:

  • Os eventos são executados do elemento mais alto para o elemento de destino.
  • O evento no elemento de destino será executado novamente.
  • A propagação de eventos de bubbling será executada até o elemento superior.

A propagação do evento pode ser interrompida chamando `event.stopPropogation` no manipulador de eventos.

Quais são as promises em JavaScript?

O objeto Promise é utilizado para operações assíncronas, que são concluídas no futuro com sucesso ou falha.

Uma promise pode estar em um dos seguintes estados:

  • Pendente: quando a operação está em andamento.
  • Cumprida: quando a operação é concluída com sucesso. Os resultados (se houver) estarão disponíveis neste estado.
  • Rejeitada: quando a operação falha, com a respectiva razão (erro) disponível.

Vamos observar exemplos de casos de sucesso e falha:

// Promise que será concluída com sucesso
const successPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve({ message: "Concluído com sucesso" });
  }, 300);
});
successPromise
  .then((data) => {
    console.log(data);
  })
  .catch((error) => {
    console.log(error);
  });

// Promise que será concluída com falha
const failurePromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(new Error("Falhando a promise para testes"));
  }, 300);
});
failurePromise
  .then((data) => {
    console.log(data);
  })
  .catch((error) => {
    console.log(error);
  });

Encadeamentos de `then` podem ser adicionados para lidar com dados sucessivamente, aceitando dados retornados pelo `then` anterior.

Explicar os diferentes tipos de escopo em JavaScript

Existem dois tipos principais de escopo em JavaScript: escopo global e escopo local. Além disso, temos o escopo de função e escopo de bloco, que se referem aos escopos locais de `var` e `let`/`const`, respectivamente.

O que são funções de auto-invocação?

Funções de auto-invocação são funções anônimas que são executadas imediatamente após a sua criação. Veja alguns exemplos:

// Sem parâmetros
(function sayHello() {
  console.log("Olá Mundo!");
})();

// Com parâmetros
(function add(a, b) {
  console.log("Soma", a + b);
})(1, 2);

Parâmetros podem ser passados para funções de auto-invocação.

O que são funções de seta?

Funções de seta são uma sintaxe mais curta para funções normais, com algumas diferenças. Elas se comportam como funções normais em casos gerais de uso, sendo úteis como callbacks. Veja a sintaxe:

// Funções de seta retornam por padrão sem chaves
let add = (a, b) => a + b;

console.log(add(1, 2));

As principais diferenças entre funções de seta e funções normais são:

  • Funções de seta não possuem sua própria associação com o `this`. A palavra-chave `this` em funções de seta se refere ao `this` de seu escopo pai.
  • Funções de seta não podem ser utilizadas como funções construtoras.

O que são callbacks?

Um callback é uma função que é passada como argumento para outra função e é invocada dentro dela. É um padrão comum em JavaScript. Veja um exemplo:

function exemplo(a, b, callback) {
  const result = a + b;
  callback(result);
}

function finished(result) {
  console.log("Terminado com", result);
}

exemplo(1, 2, finished);

A função `finished` é passada como callback para a função `exemplo`. `finished` é invocada com o resultado após alguma ação ser executada. Callbacks são comuns em operações assíncronas como promises, `setTimeout`, etc.

Quais são os diferentes tipos de erros?

Vamos ver alguns erros comuns em JavaScript:

`ReferenceError`: ocorre quando tentamos acessar uma variável inexistente.

`TypeError`: é lançado quando um erro não se encaixa em outros tipos específicos. Também ocorre quando tentamos realizar uma ação incompatível com um tipo de dado.

`SyntaxError`: ocorre quando a sintaxe do código JavaScript não está correta.

Existem outros tipos de erro, mas esses são os mais comuns em JavaScript.

Quais são os diferentes escopos de variáveis em JavaScript?

Em JavaScript, variáveis declaradas com `var` têm escopo de função e variáveis declaradas com `let` e `const` têm escopo de bloco.

Mais detalhes sobre escopos de variáveis podem ser encontrados na questão 17.

O que são caracteres de escape em JavaScript?

A barra invertida (\) é o caractere de escape em JavaScript, utilizada para exibir caracteres especiais que geralmente não podem ser impressos diretamente. Por exemplo, para exibir um apóstrofo (‘) dentro de uma string, usamos o caractere de escape para impedir que a string termine no