Pourquoi «use strict» améliore les performances 10x dans cet exemple?

128

Suite à la question Extension des performances de String.prototype, je suis vraiment intrigué, car le simple fait d'ajouter "use strict"à une String.prototypeméthode améliore les performances 10 fois. L' explication de bergi est courte et ne me l'explique pas. Pourquoi y a-t-il une différence si dramatique entre deux méthodes presque identiques, qui ne diffèrent que par "use strict"le haut? Pouvez-vous expliquer plus en détail et avec la théorie derrière cela?

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');

Résultat:

proto: 101 ms
proto-strict: 7.5 ms
exebook
la source
1
Pouvez-vous faire un test avec this[i] === charet voir si vous obtenez la même différence?
Niet the Dark Absol
1
J'ai testé avec this[i] === chardans un environnement DOM et le résultat est le même
Cristian Traìna
2
L'explication de bergi dit que lorsque vous appelez la countfonction, le thisparamètre doit être converti en un objet chaîne au lieu d'une chaîne littérale alors qu'en mode strict, il n'est pas nécessaire de le faire pour fonctionner correctement. Pourquoi c'est le cas me dépasse, je suis très intéressé par la réponse.
Nick Larsen
3
@NickLarsen: C'est juste comment la langue a été spécifiée. Traditionnellement, JS s'assurait que vous ayez toujours un objet en tant que this, mais en mode strict, il saute cette étape, vous obtenez donc la chaîne primitive , ou tout ce qui a été prévu this.
6
Il est temps de mettre "use strict";partout les garçons! Goooold
Jonathan

Réponses:

155

En mode strict, le thiscontexte n'est pas forcé d'être un objet. Si vous appelez une fonction sur un non-objet, ce thisne sera que ce non-objet.

En revanche, en mode non strict, le thiscontexte est toujours d'abord enveloppé dans un objet s'il n'est pas déjà un objet. Par exemple, (42).toString()encapsule d'abord 42un Numberobjet, puis appelle Number.prototype.toStringavec l' Numberobjet comme thiscontexte. En mode strict, le thiscontexte n'est pas touché et appelle simplementNumber.prototype.toString avec 42comme thiscontexte.

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

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

Dans votre cas, la version en mode non strict passe beaucoup de temps à envelopper et à dérouler les primitives stringdansString encapsuler et à wrappers d'objets et inversement. La version en mode strict fonctionne par contre directement sur la primitive string, ce qui améliore les performances.

Mattias Buelens
la source
1
Et la suppression de withaide également un peu pour chaque iirc de recherche de variable.
zzzzBov
2
@zzzzBov incorrect. La suppression de withaide énormément car elle permet au navigateur de raisonner quelle expression de variable fait référence à quelle variable.
John Dvorak
2
Il me semble peu intuitif que le non-objet thissoit "plus strict" que toujours l'objet this.
IllidanS4 veut que Monica revienne le
2
@ IllidanS4: Il s'agit principalement de cas où thisest nullouundefined , qui serait l'objet global en mode bâclé.
Bergi
6
@ IllidanS4: Pensez-y comme "réel this" ou "wrapper this" si vous le souhaitez. Les wrappers d'objets sont un kludge qui n'aurait jamais dû exister, il est donc logique que le mode strict les évite davantage lorsque cela est possible.
Ry-