Por que "use strict" melhora o desempenho em 10x neste exemplo?

128

Após a pergunta Estendendo o desempenho do String.prototype , estou realmente intrigado, porque apenas adicionar "use strict"um String.prototypemétodo melhorou o desempenho 10 vezes. A explicação de Bergi é curta e não me explica. Por que existe uma diferença tão dramática entre dois métodos quase idênticos, que apenas diferem no "use strict"topo? Você pode explicar com mais detalhes e com a teoria por trás disso?

String.prototype.count = function(char) {
  var n = 0;
  for (var i = 0; i < this.length; i++)
    if (this[i] == char) n++;
  return n;
};

String.prototype.count_strict = function(char) {
  "use strict";
  var n = 0;
  for (var i = 0; i < this.length; i++)
    if (this[i] == char) n++;
  return n;
};
// Here is how I measued speed, using Node.js 6.1.0

var STR = '0110101110010110100111010011101010101111110001010110010101011101101010101010111111000';
var REP = 1e4;

console.time('proto');
for (var i = 0; i < REP; i++) STR.count('1');
console.timeEnd('proto');

console.time('proto-strict');
for (var i = 0; i < REP; i++) STR.count_strict('1');
console.timeEnd('proto-strict');

Resultado:

proto: 101 ms
proto-strict: 7.5 ms
exebook
fonte
1
Você pode fazer um teste this[i] === chare ver se obtém a mesma diferença?
Niet the Dark Absol
1
I testados com this[i] === charnum ambiente de DOM e o resultado é o mesmo
Cristian Traina
2
A explicação de bergi diz que quando você chama a countfunção, o thisparâmetro deve ser convertido em um objeto de string em vez de em um literal de string, enquanto no modo estrito não é necessário para operar corretamente. Por que esse é o caso está além de mim, estou muito interessado na resposta.
Nick Larsen
3
@NickLarsen: É assim que a linguagem foi especificada. Tradicionalmente, o JS garantiria que você sempre tivesse um objeto como this, mas, no modo estrito, pula essa etapa, para que você obtenha a string primitiva ou o que for fornecido this.
6
É hora de colocar em "use strict";todos os lugares meninos! Goooold
Jonathan

Respostas:

155

No modo estrito, o thiscontexto não é forçado a ser um objeto. Se você chamar uma função em um não-objeto, thisserá apenas esse não-objeto.

Por outro lado, no modo não estrito, o thiscontexto é sempre primeiro envolvido em um objeto se ele ainda não é um objeto. Por exemplo, (42).toString()primeiro envolve 42um Numberobjeto e depois chama Number.prototype.toStringo Numberobjeto como thiscontexto. No modo estrito, o thiscontexto é deixado chamadas intocadas e apenas Number.prototype.toStringcom 42como thiscontexto.

(function() {
  console.log(typeof this);
}).call(42); // 'object'

(function() {
  'use strict';
  console.log(typeof this);
}).call(42); // 'number'

No seu caso, a versão em modo não estrito gasta muito tempo agrupando e desembrulhando strings primitivos em Stringinvólucros de objetos e vice-versa. A versão em modo estrito, por outro lado, funciona diretamente no primitivo string, o que melhora o desempenho.

Mattias Buelens
fonte
1
E a remoção de withtambém ajuda um pouco para cada pesquisa de variáveis ​​IIRC.
zzzzBov
2
@zzzzBov incorreto. A remoção de withajuda imensamente , pois permite ao navegador raciocinar qual expressão de variável se refere a qual variável.
John Dvorak
2
Parece-me pouco intuitivo que o não-objeto thisseja "mais rigoroso" que o sempre-objeto this.
IllidanS4 quer Monica de volta
2
@ IllidanS4: Trata-se principalmente de casos em que thisé nullou undefined, que seria o objeto global no modo desleixado.
Bergi 17/07/2016
6
@ IllidanS4: Pense nisso como "real this" vs. "invólucro this", se quiser. Os wrappers de objetos são um kludge que nunca deveria existir, portanto, faz sentido que o modo estrito os evite mais quando possível.
Ry-