Strict mode

O strict mode do ECMAScript 5 é uma forma de optar por uma variante restrita do JavaScript. O strict mode não é apenas um subconjunto: ele intencionalmente tem semânticas diferentes do código normal. Navegadores que não suportam strict mode executarão código strict mode com um comportamento diferente dos navegadores que suportam, então não confie no strict mode sem testar o suporte a funcionalidades dos aspectos relevantes do strict mode. Código strict mode e código não-strict mode podem coexistir, então scripts podem optar pelo strict mode incrementalmente.

O strict mode faz várias mudanças nas semânticas normais do JavaScript. Primeiro, o strict mode elimina alguns erros silenciosos do JavaScript fazendo-os lançar exceções. Segundo, o strict mode evita equívocos que dificultam que motores JavaScript realizem otimizações: código strict mode pode às vezes ser feito para executar mais rápido que código idêntico não-strict mode. Terceiro, strict mode proíbe algumas sintaxes que provavelmente serão definidas em versões futuras do ECMAScript.

Veja transitioning to strict mode se você quer mudar seu código para funcionar na variante restrita do JavaScript.

Invocando strict mode

Strict mode se aplica a scripts inteiros ou funções individuais. Ele não se aplica a declarações de bloco fechadas em chaves {}; tentar aplicá-lo a tais contextos não faz nada. Código eval, código Function, atributos event handlers, strings passadas para setTimeout e coisas do tipo são scripts inteiros, e invocar strict mode neles funciona como esperado.

Strict mode para scripts

Para invocar strict mode para um script inteiro, coloque exatamente a declaração "use strict"; (ou 'use strict';) antes de qualquer outra declaração.

js
// Sintaxe strict mode para todo o script
"use strict";
var v = "Oi! Eu sou um script strict mode!";

Essa sintaxe tem uma armadilha que já pegou um site grande: não é possível concatenar cegamente scripts não-conflitantes. Considere concatenar um script strict mode com um script não-strict mode: a concatenação inteira parece strict! O inverso também é verdade: não-strict mais strict parece não-strict. Concatenar scripts strict mode com outros é bom, e concatenar scripts não-strict mode é bom. Simplesmente concatenar scripts strict e não-strict é problemático. Portanto é recomendado que você ative strict mode função-por-função (pelo menos durante o período de transição).

Você também pode usar a abordagem de embrulhar o conteúdo inteiro de um script em uma função e fazer a função externa usar strict mode. Isto elimina o problema da concatenação, mas significa que você tem que exportar explicitamente quaisquer variáveis globais fora do escopo da função.

Strict mode para funções

Da mesma forma, para invocar strict mode para uma função, coloque exatamente a declaração "use strict"; (ou 'use strict';) no corpo da função antes de qualquer outra declaração.

js
function strict() {
  // Sintaxe strict mode a nível de função
  "use strict";
  function nested() {
    return "E eu também!";
  }
  return "Oi! Eu sou uma função strict mode!  " + nested();
}
function notStrict() {
  return "Eu não sou strict.";
}

Mudanças no strict mode

Strict mode muda tanto a sintaxe quando o comportamento em tempo de execução. Mudanças geralmente se encaixam em uma destas categorias: mudanças que convertem equívocos em erros (como erros de sintaxe ou em tempo de execução), mudanças que simplificam como uma variável particular para um dado uso de um nome é computada, mudanças que simplificam eval e arguments, mudanças que facilitam JavaScript "seguro", e mudanças que antecipam a evolução futura do ECMAScript.

Convertendo equívocos em erros

Strict mode transforma em erros alguns equívocos anteriormente aceitos. O JavaScript foi projetado para ser fácil para novos desenvolvedores, e algumas vezes ele dá semânticas de não-erros a operações que deveriam ser erros. Algumas vezes isso resolve o problema pontual, mas outras vezes cria problemas piores no futuro. Strict mode trata esses equívocos como erros para que sejam descobertos e consertados prontamente.

Primeiro, o strict mode impossibilita criar variáveis globais acidentalmente. Em JavaScript normal, cometer um erro de digitação ao digitar uma variável em uma atribuição cria uma nova propriedade no objeto global e continua a "funcionar" (embora falhas futuras sejam possíveis: provavelmente, em JavaScript moderno). Atribuições que acidentalmente criariam variáveis globais lançam exceções em vez disso no strict mode:

js
"use strict";
// Assumindo que uma variável global variavelErrada existe
variavellErrada = 17; // esta linha lança ReferenceError por conta do
// erro de digitação da variável

Segundo, strict mode faz com que atribuições que outrora falhariam silenciosamente lançar uma exceção. Por exemplo, NaN é uma variável global não-atribuível. Em código normal, atribuir a NaN não faz nada; o desenvolvedor não recebe qualquer resposta de falha. No strict mode, atribuir a NaN lança uma exceção. Qualquer atribuição que falha silenciosamente em código normal (atribuir a uma propriedade não-atribuível, atribuir a uma propriedade getter-only, atribuir a uma propriedade de um objeto não-extensível) lançarão exceções em strict mode:

js
"use strict";

// Atribuir a uma propriedade não-atribuível
var obj1 = {};
Object.defineProperty(obj1, "x", { value: 42, writable: false });
obj1.x = 9; // lança TypeError

// Atribuir a uma propriedade getter-only
var obj2 = {
  get x() {
    return 17;
  },
};
obj2.x = 5; // lança TypeError

// Atribuir a uma nova propriedade de um objeto não-extensível
var fixed = {};
Object.preventExtensions(fixed);
fixed.newProp = "ohai"; // lança TypeError

Terceiro, strict mode faz com que tentativas de deletar propriedades não-deletáveis lancem exceções (onde anteriormente a tentativa simplesmente não faria efeito):

js
"use strict";
delete Object.prototype; // lança TypeError

Quarto, strict mode anterior ao Gecko 34 requer que todas as propriedades nomeadas em um objeto literal sejam únicas. Código normal pode duplicar nomes de propriedades, sendo que a última ocorrência determina o valor da propriedade. Mas como apenas o último faz algo, a duplicação é meramente um vetor de bugs, se o código for modificado para mudar o valor da propriedade por outro meio que não modificando a última instância. Nomes de propriedades duplicados são erro de sintaxe em strict mode:

Nota: Não é mais o caso no ECMAScript 6 (Erro do Firefox 1041128).

js
"use strict";
var o = { p: 1, p: 2 }; // !!! erro de sintaxe

Quinto, strict mode exige que nomes de parâmetros de função sejam únicos. Em código normal, o último argumento duplicado esconde argumentos anteriores com nome semelhante. Estes argumentos anteriores continuam disponíveis através de arguments[i], portanto não são completamente inacessíveis. Entretanto, esta ocultação faz pouco sentido e provavelmente é indesejável (pode esconder um erro de digitação, por exemplo), então em strict mode nomes de argumentos duplicados são erro de sintaxe:

js
function sum(a, a, c) {
  // !!! erro de sintaxe
  "use strict";
  return a + b + c; // errado se este código executasse
}

Sexto, strict mode em ECMAScript 5 proíbe sintaxe octal. Sintaxe ocatal não é parte do ECMAScript 5, mas é suportado em todos os navegadores prefixando o número octal com um zero: 0644 === 420 e "\045" === "%". No ECMAScript 6, número octal é suportado prefixando um número com "0o":

js
var a = 0o10; // ES6: Octal

Novos desenvolvedores algumas vezes acham que um prefixo zero não tem significado semântico, então eles o utilizam como mecanismo de alinhamento — mas isso muda o significado do número! Sintaxe octal é raramente útil e pode ser usada equivocadamente, então o strict mode torna octal erro de sintaxe:

js
"use strict";
var sum =
  015 + // !!! erro de sintaxe
  197 +
  142;

Sétimo, strict mode em ECMAScript 6 proíbe definir propriedades em valores primitivos. Sem strict mode, definir uma propriedade é simplesmente ignorado; com strict mode, no entanto, um TypeError é lançado.

js
(function () {
  "use strict";

  false.true = ""; // TypeError
  (14).navegandoPara = "casa"; // TypeError
  "com".voce = "distante"; // TypeError
})();

Simplificando usos de variáveis

Strict mode simplifica como nomes de variáveis se mapeiam a definições de variáveis particulares no código. Muitas otimizações de compiladores dependem da habilidade de dizer que variável X está guardada naquele local; isto é crítico para otimizar completamente o código JavaScript. JavaScript às vezes torna este mapeamento básico de nome para definição de variável no código impossível de executar até o tempo de execução. Strict mode remove a maioria dos casos onde isso acontece, então o compilador pode otimizar melhor código strict mode.

Primeiro, strict mode proíbe with. O problema com with é que qualquer nome dentro do block pode mapear tanto para uma propriedade do objeto passado quanto para uma variável no escopo envolvente (ou até global), em tempo de execução: é impossível saber qual de antemão. Strict mode torna with um erro de sintaxe, então não há chance de um nome em um with referir-re a uma localização deconhecida em tempo de execução:

js
"use strict";
var x = 17;
with (obj) {
  // !!! erro de sintaxe
  // Se isto não fosse strict mode, isso seria var x ou obj.x?
  // No geral, é impossível saber sem executar o código, então
  // o nome não pode ser otimizado.
  x;
}

A alternativa simples de atribuir o objeto a uma variável de nome curto, e então acessar a propriedade correspondente naquela variável, está pronta para substituir with.

Segundo, eval de código strict mode não introduz novas variáves no escopo envolvente. Em código normal, eval("var x;") introduz uma variável x na função envolvente ou no escopo global. Isto significa que, no geral, em uma função que contém uma chamada para eval, todo nome que não se refere a um argumento ou variável local deve ser mapeado para uma definição particular em tempo de execução (porque aquele eval pode ter introduzido uma nova variável que esconderia a variável externa). No strict mode, eval cria variáveis apenas para o código sendo avaliado, portanto eval não pode afetar se um nome se refere a uma variável externa ou alguma variável local:

js
var x = 17;
var evalX = eval("'use strict'; var x = 42; x");
console.assert(x === 17);
console.assert(evalX === 42);

Relacionadamente, se a função eval for invocada por uma expressão da forma eval(...) em código strict mode, o código será avaliado como código strict mode. O código pode invocar strict code explicitamente, mas não é necessário.

js
function strict1(str) {
  "use strict";
  return eval(str); // str será tratado como código strict mode
}
function strict2(f, str) {
  "use strict";
  return f(str); // não é eval(...): str é strict se e
  // somente se invocar strict mode
}
function nonstrict(str) {
  return eval(str); // str é strict se e somente se
  // invocar strict mode
}

strict1("'Código strict mode!'");
strict1("'use strict'; 'Código strict mode!'");
strict2(eval, "'Código não-strict.'");
strict2(eval, "'use strict'; 'Código strict mode!'");
nonstrict("'Código não-strict.'");
nonstrict("'use strict'; 'Código strict mode!'");

Portanto nomes em código eval de strict mode se comportam da mesma forma que nomes em código strict mode que não estão sendo avaliados como resultado de eval.

Terceiro, strict mode proíbe deletar nomes simples. delete name em strict mode é erro de sintaxe:

js
"use strict";

var x;
delete x; // !!! erro de sintaxe

eval("var y; delete y;"); // !!! erro de sintaxe

Tornando eval e arguments mais simples

Strict mode torna arguments e eval menos estranhamente mágicos. Ambos envolvem uma quantidade considerável de comportamento mágico em código normal: eval para adicionar ou remover bindings e para mudar valores de bindings, e arguments com suas propriedades indexadas como alias de argumentos nomeados. Strict mode dá grandes passos na direção de tratar eval e arguments como palavras-chave, embora reparos completos não venham até uma futura edição do ECMAScript.

Primeiro, os nomes eval e arguments não podem ser vinculados ou atribuídos na sintaxe da linguagem. Todas as tentativas são erros de sintaxe:

js
"use strict";
eval = 17;
arguments++;
++eval;
var obj = { set p(arguments) {} };
var eval;
try {
} catch (arguments) {}
function x(eval) {}
function arguments() {}
var y = function eval() {};
var f = new Function("arguments", "'use strict'; return 17;");

Segundo, código strict mode não faz alias para propriedades de objetos arguments criados dentro de si. Em código normal dentro de uma função cujo primeiro argumento é arg, definir arg também define arguments[0], e vice-versa (a menos que nenhum argumento seja fornecido ou arguments[0] seja deletado). Objetos arguments para funções strict mode armazenam os argumentos originais quando a função é invocada. arguments[i] não rastreia o valor do argumento nomeado correspondente, nem um argumento nomeado rastreia o valor do arguments[i] correspondente.

js
function f(a) {
  "use strict";
  a = 42;
  return [a, arguments[0]];
}
var pair = f(17);
console.assert(pair[0] === 42);
console.assert(pair[1] === 17);

Terceiro, arguments.callee não é mais suportado. Em código normal, arguments.callee se refere à função envolvente. Esse caso de uso é frágil: apenas nomeie a função envolvente! Além disso, arguments.callee prejudica substancialmente otimizações como tornar funções inline porque deve ser possível fornecer uma referência à função não-inline se arguments.callee for acessado. arguments.callee para funções strict mode é uma propriedade não-deletável que lança exceção quando atribuída ou recuperada:

js
"use strict";
var f = function () {
  return arguments.callee;
};
f(); // lança TypeError

Tornando JavaScript "seguro"

Strict mode torna mais fácil escrever JavaScript "seguro". Alguns websites agora oferecem formas de escrever JavaScript que será executado pelo website em nome de outros usuários. JavaScript nos navegadores pode acessar informações privadas do usuário, então tal JavaScript deve ser parcialmente transformado antes de executado, para barrar acesso a funcionalidades proibidas. A flexibilidade do JavaScript torna isso efetivamente impossível sem várias verificações em tempo de execução. Algumas funções da linguagem são tão pervasivas que fazer verificações em tempo de execução tem um custo de desempenho considerável. Alguns ajustes do strict mode, mais a exigência de que o JavaScript submetido por usuários seja código strict mode e invocá-lo de uma certa maneira, reduz substancialmente a necessidade de realizar verificações em tempo de execução.

Primeiro, o valor passado como this para uma função em strict mode não é forçado a ser um objeto (entenda-se "encaixotamento"). Para uma função normal, this é sempre um objeto: seja o objeto fornecido se chamado com um this cujo valor é um objeto; o valor, encaixotado, se chamado com um this Boolean, string ou número; ou o objeto global se chamado com um this undefined ou null. Use call, apply, ou bind para especificar um determinado this. O encaixotamento automático não apenas é um custo de desempenho, como também expor o objeto global em navegadores é um risco de segurança, porque o objeto global oferece acesso a funcionalidades que ambientes JavaScript "seguros" devem restringir. Assim, para uma função strict mode, o this especificado não é encaixotado em um objeto, e se não especificado, this será undefined:

js
"use strict";
function fun() {
  return this;
}
console.assert(fun() === undefined);
console.assert(fun.call(2) === 2);
console.assert(fun.apply(null) === null);
console.assert(fun.call(undefined) === undefined);
console.assert(fun.bind(true)() === true);

Isso significa, além de outras coisas, que em navegadores não é mais possíveis referenciar o objeto window através de this dentro de uma função strict mode.

Segundo, em strict mode não é mais possível "caminhar" a pilha JavaScript via extensões do ECMAScript comumente implementadas. Em código normal com essas extensões, quando uma funçao fun está no meio de ser chamada, fun.caller é a função que chamou fun mais recentemente, e fun.arguments é o arguments para aquela invocação de fun. Ambas as extensões são problemáticas para JavaScript "seguro" porque permitem que código "seguro" acesse funções "privilegiadas" e seus argumentos (potencialmente inseguros). Se fun estiver em strict mode, tanto fun.caller quando fun.arguments são propriedades não-deletáveis que lançam exceção quando definidas ou recuperadas:

js
function restrita() {
  "use strict";
  restrita.caller; // lança TypeError
  restrita.arguments; // lança TypeError
}
function invocadorPrivilegiado() {
  return restrita();
}
invocadorPrivilegiado();

Terceiro, arguments para funções strict mode não oferecem mais acesso às variáveis da chamada de função correspondente. Em algumas implementações antigas do ECMAScript, arguments.caller era um objeto cujas propriedades faziam alias de variáveis naquela função. Isso é um risco de segurança porque quebra a habilidade de esconder valores privilegiados via abstração de função; isso também exclui a maioria das otimizações. Por essas razões, nenhum navegador moderno implementa isso. Ainda assim, por causa de sua funcionalidade histórica, arguments.caller para uma função strict mode também é uma propriedade não-deletável que lança exceção quando definida ou recuperada:

js
"use strict";
function fun(a, b) {
  "use strict";
  var v = 12;
  return arguments.caller; // lança TypeError
}
fun(1, 2); // não expõe v (or a or b)

Pavimentando o caminho para versões futuras do ECMAScript

Versões futuras do ECMAScript provavelmente introduzirão nova sintaxe, e o strict mode no ECMAScript 5 aplica algumas restrições para facilitar essa transição. Será mais fácil fazer algumas mudanças se as fundações dessas mudanças forem proibidas no strict mode.

Primeiro, no strict mode, uma pequena lista de identificadores tornam-se palavras-chave reservadas. Estas palavras são implements, interface, let, package, private, protected, public, static e yield. Em strict mode, portanto, você não pode nomear ou usar variáveis ou argumentos com esses nomes.

js
function package(protected) {
  // !!!
  "use strict";
  var implements; // !!!

  // !!!
  interface: while (true) {
    break interface; // !!!
  }

  function private() {} // !!!
}
function fun(static) {
  "use strict";
} // !!!

Duas ressalvas específicas à Mozilla: primeiro, se seu código for JavaScript 1.7 ou superior (por exemplo em código chrome ou usando o <script type=""> correto) e for strict mode, let e yield têm a funcionaldade que eles tiveram desde que essas palavras-chave foram introduzidas pela primeira vez. Mas código strict mode na web, carregado com <script src=""> ou <script>...</script>, não serão capazes de usar let/yield como identificadores. Segundo, enquanto o ES5 reserva incondicionalmente as palavras class, enum, export, extends, import e super, a Mozilla reservava-as apenas em strict mode antes do Firefox 5.

Segundo, strict mode proíbe declaração de funções fora do nível superior de um script ou função. Em código normal em navegadores, declarações de funções são permitidas "em todo lugar". Isso não é parte do ES5 (nem do ES3)! É uma extensão com semânticas incompatíveis em diferentes navegadores. Edições futuras do ECMAScript promissoramente especificarão novas semânticas para declarações de funções fora do nível superior de um script ou função. Proibir tais declarações de funções em strict mode "limpa a mesa" para especificação em uma release futura do ECMAScript:

js
"use strict";
if (true) {
  function f() {} // !!! erro de sintaxe
  f();
}

for (var i = 0; i < 5; i++) {
  function f2() {} // !!! erro de sintaxe
  f2();
}

function baz() {
  // correto
  function eit() {} // erro de sintaxe
}

Essa proibição não é adequada ao strict mode porque tais declarações de função são uma extensão de ES5 básico. Mas é a recomendação do comitê do ECMAScript e os navegadores a implementarão.

Strict mode em navegadores

Os principais navegadores agora implementam strict mode. Entretanto, não dependa deles cegamente já que ainda há várias versões de navegadores utilizadas por aí que têm apenas suporte parcial ao strict mode ou que não suportam de forma alguma (ex.: Internet Explorer abaixo da versão 10!). Strict mode muda semântica. Depender dessas mudanças causará equívocos e erros em navegadores que não implementam strict mode. Tenha cautela ao usar strict mode e gere confiança no strict mode com testes de funcionalidades que verificam se partes relevantes do strict mode estão implementadas. Finalmente, certifique-se de testar seu código em navegadores que suportam e não suportam strict mode. Se você testar apenas em navegadores que não suportam strict mode, você provavelmente terá problemas em navegadores que suportam e vice-versa.

Especificações

Specification
ECMAScript Language Specification

Veja também