¿Por qué "usar estricto" mejora el rendimiento 10 veces en este ejemplo?

128

Siguiendo la pregunta Extendiendo String.prototype performance Estoy realmente intrigado, porque solo agregar "use strict"a un String.prototypemétodo mejoró el rendimiento 10 veces. La explicación de bergi es breve y no me la explica. ¿Por qué hay una diferencia tan dramática entre dos métodos casi idénticos, que solo difieren en "use strict"la parte superior? ¿Puedes explicar con más detalle y con la teoría detrás de esto?

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
fuente
1
¿Puedes hacer una prueba this[i] === chary ver si obtienes la misma diferencia?
Niet the Dark Absol
1
Probé con this[i] === charen un entorno de DOM y el resultado es el mismo
Cristian Traina
2
La explicación de bergi dice que cuando se llama a la countfunción, el thisparámetro debe convertirse en un objeto de cadena en lugar de un literal de cadena, mientras que en modo estricto no es necesario para funcionar correctamente. Por qué este es el caso está más allá de mí, estoy muy interesado en la respuesta.
Nick Larsen
3
@NickLarsen: así es como se especificó el idioma. Tradicionalmente, JS se aseguraba de que siempre tuviera un objeto como this, pero en modo estricto omite ese paso, por lo que obtiene la cadena primitiva , o lo que sea que se proporcionó this.
66
¡Es hora de poner a "use strict";todos lados muchachos! Goooold
Jonathan

Respuestas:

155

En modo estricto, el thiscontexto no está obligado a ser un objeto. Si llama a una función en un no objeto, thissolo será ese no objeto.

Por el contrario, en modo no estricto, el thiscontexto siempre se envuelve primero en un objeto si aún no es un objeto. Por ejemplo, (42).toString()primero se envuelve 42en un Numberobjeto y luego se llama Number.prototype.toStringcon el Numberobjeto como thiscontexto. En modo estricto, el thiscontexto se deja sin tocar las llamadas y sólo Number.prototype.toStringcon 42como thiscontexto.

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

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

En su caso, la versión en modo no estricto pasa mucho tiempo envolviendo y desenvolviendo mensajes primitivos stringen Stringenvoltorios de objetos y viceversa. La versión en modo estricto, por otro lado, trabaja directamente en la primitiva string, lo que mejora el rendimiento.

Mattias Buelens
fuente
1
Y la eliminación de withtambién ayuda un poco para cada búsqueda variable de iirc.
zzzzBov
2
@zzzzBov incorrecto. La eliminación de la withayuda es inmensa, ya que permite al navegador razonar qué expresión variable se refiere a qué variable.
John Dvorak
2
Me parece poco intuitivo que no objeto thises "más estricto" que siempre objeto this.
IllidanS4 quiere que Monica regrese el
2
@ IllidanS4: se trata principalmente de casos en los que thises nullo undefined, que sería el objeto global en modo descuidado.
Bergi
66
@ IllidanS4: piense en ello como "real this" versus "envoltorio this" si lo desea. Los envoltorios de objetos son un error que nunca debería haber existido, por lo que tiene sentido que el modo estricto los evite más cuando sea posible.
Ry-