O modo estrito ('use strict') é uma funcionalidade introduzida no ECMAScript 5 que permite optar por uma variante restrita do JavaScript. Ao ativar o modo estrito, o interpretador passa a aplicar regras mais rígidas de sintaxe e semântica, transformando erros silenciosos em erros lançados, proibindo certas construções e impedindo ações que podem levar a bugs sutis. Essa diretiva é essencial para escrever código moderno e robusto, e é recomendada em todos os novos projetos.

Nesta aula, vamos detalhar o que muda com o modo estrito, os motivos para ativá-lo e as diferenças de comportamento em relação ao modo normal. Ao final, você terá uma compreensão sólida de como usar o 'use strict' para melhorar a qualidade do seu código JavaScript.

O que muda

O modo estrito altera vários comportamentos do JavaScript. Em vez de listar exaustivamente todas as mudanças, vamos focar nas mais relevantes para o dia a dia do desenvolvimento. A ativação é feita com a string 'use strict' no início de um script ou de uma função. Quando colocada no início de um arquivo, aplica-se a todo o código; quando dentro de uma função, apenas àquela função (e às funções aninhadas).

Uma das mudanças mais significativas é que o modo estrito elimina alguns erros silenciosos, fazendo com que eles lancem exceções. Por exemplo, atribuir valor a uma variável não declarada, que no modo normal cria uma variável global implicitamente, no modo estrito lança um ReferenceError. Isso ajuda a evitar poluição do escopo global e bugs decorrentes de erros de digitação.

// modo normal
nome = 'Maria'; // cria uma variável global (em navegadores)
console.log(nome); // 'Maria'

// modo estrito
'use strict';
nome = 'Maria'; // ReferenceError: assignment to undeclared variable nome

Outra mudança importante é que o modo estrito proíbe a utilização de palavras reservadas para futuras versões do ECMAScript, como implements, interface, package, private, protected, public, static, let, yield (em algumas versões). Isso garante que o código não quebre quando novas funcionalidades forem adicionadas à linguagem.

Além disso, o modo estrito torna mais fácil escrever código seguro, pois proíbe o uso de with, que é considerado perigoso e prejudicial à performance. A declaração with adiciona um objeto ao escopo, o que pode causar ambiguidades e dificultar a otimização do código. No modo estrito, usar with resulta em um erro de sintaxe.

// modo normal
var obj = { x: 10 };
with (obj) {
  console.log(x); // 10
}

// modo estrito
'use strict';
var obj = { x: 10 };
with (obj) { // SyntaxError: Strict mode code may not include a with statement
  console.log(x);
}

Por que ativar

Ativar o modo estrito é uma prática recomendada por diversos motivos. Primeiramente, ele ajuda a detectar erros cedo, transformando erros silenciosos em exceções que podem ser capturadas e corrigidas durante o desenvolvimento. Isso reduz a probabilidade de bugs que passam despercebidos em produção.

Em segundo lugar, o modo estrito melhora a segurança do código. Ao proibir o acesso a propriedades de objetos como this em funções chamadas sem contexto, ele evita que o valor global (window no navegador, global no Node.js) seja usado indevidamente. Em funções normais, no modo não estrito, this é forçado a ser o objeto global quando a função é chamada sem um receptor; no modo estrito, this permanece undefined. Isso é mais lógico e evita modificações acidentais no objeto global.

function mostrarThis() {
  console.log(this);
}

mostrarThis(); // no modo normal: Window (ou global); no modo estrito: undefined

'use strict';
function mostrarThisEstrito() {
  console.log(this);
}

mostrarThisEstrito(); // undefined

Além disso, o modo estrito torna o JavaScript mais alinhado com boas práticas de programação, como declarar todas as variáveis antes de usá-las. Isso é especialmente importante em equipes, pois o código fica mais previsível e fácil de revisar.

Outro benefício é a compatibilidade futura. Ao proibir o uso de palavras reservadas, o modo estrito garante que o código não quebre quando novas funcionalidades forem adicionadas ao ECMAScript. Isso é crucial para bibliotecas e frameworks que precisam manter compatibilidade com várias versões do JavaScript.

Diferenças de comportamento

Vamos explorar algumas diferenças específicas de comportamento entre o modo estrito e o modo não estrito. Uma delas é a atribuição a propriedades não graváveis ou getters sem setter. No modo normal, essa atribuição falha silenciosamente; no modo estrito, lança um TypeError.

// modo normal
var obj = {};
Object.defineProperty(obj, 'x', { value: 42, writable: false });
obj.x = 99; // silenciosamente ignora
console.log(obj.x); // 42

// modo estrito
'use strict';
var obj = {};
Object.defineProperty(obj, 'x', { value: 42, writable: false });
obj.x = 99; // TypeError: Cannot assign to read only property 'x' of object

Outra diferença é a atribuição a propriedades de um objeto não extensível. No modo normal, a atribuição é ignorada; no modo estrito, lança um TypeError.

// modo normal
var obj = {};
Object.preventExtensions(obj);
obj.x = 1; // silenciosamente ignora
console.log(obj.x); // undefined

// modo estrito
'use strict';
var obj = {};
Object.preventExtensions(obj);
obj.x = 1; // TypeError: Cannot add property x, object is not extensible

Além disso, o modo estrito altera o comportamento de eval. No modo normal, eval pode introduzir variáveis no escopo circundante; no modo estrito, eval cria variáveis apenas no escopo do próprio eval, isolando o código avaliado. Isso evita vazamentos de escopo e problemas de segurança.

// modo normal
var x = 1;
eval('var y = 2;');
console.log(y); // 2 (y vaza para o escopo externo)

// modo estrito
'use strict';
var x = 1;
eval('var y = 2;');
console.log(y); // ReferenceError: y is not defined

Outra diferença importante é que, no modo estrito, os argumentos de uma função não são sincronizados com os parâmetros nomeados. No modo normal, alterar arguments[0] altera o valor do parâmetro correspondente e vice-versa; no modo estrito, eles são independentes.

// modo normal
function soma(a, b) {
  arguments[0] = 10;
  return a + b; // a agora é 10
}
console.log(soma(1, 2)); // 12

// modo estrito
'use strict';
function somaEstrito(a, b) {
  arguments[0] = 10;
  return a + b; // a continua 1
}
console.log(somaEstrito(1, 2)); // 3

Boas práticas

Para aproveitar ao máximo o modo estrito, é recomendável ativá-lo em todos os seus arquivos JavaScript. No entanto, ao trabalhar com código legado ou com bibliotecas que não estão preparadas, você pode ativá-lo apenas em funções específicas. Uma prática comum é envolver todo o conteúdo do arquivo em uma IIFE (Immediately Invoked Function Expression) e colocar 'use strict' no início, evitando que o modo estrito afete outros scripts concatenados.

(function() {
  'use strict';
  // seu código aqui
})();

Além disso, é importante lembrar que o modo estrito é ativado por padrão em módulos ES6 (quando usamos import/export) e em classes. Portanto, se você já usa módulos, o modo estrito é aplicado automaticamente, não precisando da diretiva explícita.

Em resumo, o modo estrito é uma ferramenta valiosa para evitar erros comuns, melhorar a segurança e preparar seu código para o futuro. Adotá-lo é um sinal de maturidade no desenvolvimento JavaScript.

Referências

Exercícios

  1. O que acontece quando você tenta atribuir um valor a uma variável não declarada em modo estrito? Explique e dê um exemplo de código.

✓ Resposta: Em modo estrito, atribuir um valor a uma variável não declarada lança um ReferenceError. Exemplo:
'use strict';
x = 10; // ReferenceError: x is not defined
  • Escreva uma função que, em modo estrito, retorne undefined ao ser chamada sem um objeto receptor (ou seja, this deve ser undefined).
  • ✓ Resposta:
    'use strict';
    function mostrarThis() {
      return this;
    }
    console.log(mostrarThis()); // undefined
  • Qual é o efeito do modo estrito sobre a declaração with? Dê um exemplo.
  • ✓ Resposta: No modo estrito, a declaração with é proibida e causa um erro de sintaxe. Exemplo:
    'use strict';
    var obj = { a: 1 };
    with (obj) { // SyntaxError
      console.log(a);
    }
  • Explique a diferença entre arguments e os parâmetros nomeados no modo estrito. Dê um exemplo.
  • ✓ Resposta: No modo estrito, os valores de arguments não são sincronizados com os parâmetros nomeados. Alterar arguments[0] não altera o parâmetro correspondente. Exemplo:
    'use strict';
    function soma(a, b) {
      arguments[0] = 10;
      return a + b; // a permanece o valor original
    }
    console.log(soma(1, 2)); // 3
  • Como o modo estrito afeta a atribuição a propriedades não graváveis? Dê um exemplo.
  • ✓ Resposta: No modo estrito, tentar atribuir a uma propriedade não gravável lança um TypeError. Exemplo:
    'use strict';
    var obj = {};
    Object.defineProperty(obj, 'x', { value: 42, writable: false });
    obj.x = 99; // TypeError