Почему в этом примере «use strict» повышает производительность в 10 раз?

128

После вопроса « Расширение производительности String.prototype» я был очень заинтригован, потому что простое добавление "use strict"к String.prototypeметоду улучшило производительность в 10 раз. Объяснение по Бергам коротко и не объясняет мне. Почему существует такая резкая разница между двумя почти идентичными методами, которые отличаются только "use strict"вверху? Можете ли вы объяснить более подробно и с теорией, стоящей за этим?

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

Результат:

proto: 101 ms
proto-strict: 7.5 ms
exebook
источник
1
Можете ли вы провести тест this[i] === charи увидеть, получите ли вы такую ​​же разницу?
Niet the Dark Absol
1
Я тестировал this[i] === charв среде DOM, и результат тот же
Cristian Traìna
2
В объяснении Берги говорится, что когда вы вызываете countфункцию, thisпараметр должен быть приведен к строковому объекту, а не к строковому литералу, тогда как в строгом режиме это не обязательно для правильной работы. Почему это так, мне непонятно, мне очень интересен ответ.
Ник Ларсен
3
@NickLarsen: Это просто спецификация языка. Традиционно JS следит за тем, чтобы у вас всегда был объект as this, но в строгом режиме он пропускает этот шаг, поэтому вы получаете примитивную строку или все, что было предусмотрено this.
6
Пора расставить "use strict";везде мальчиков! Goooold
Джонатан

Ответы:

155

В строгом режиме thisконтекст не обязательно должен быть объектом. Если вы вызываете функцию для не-объекта, это thisбудет просто не-объект.

Напротив, в нестрогом режиме thisконтекст всегда сначала оборачивается в объект, если это еще не объект. Например, (42).toString()первые обертывании 42в Numberобъекте , а затем вызывают Number.prototype.toStringс Numberобъектом в качестве thisконтекста. В строгом режиме thisконтекст остается нетронутым и просто вызывается Number.prototype.toStringс контекстом 42as this.

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

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

В вашем случае версия нестрогого режима тратит много времени на перенос и развертывание примитивов stringв Stringобертки объектов и обратно. С другой стороны, версия в строгом режиме напрямую работает с примитивом string, что улучшает производительность.

Маттиас Буэленс
источник
1
Удаление withтакже немного помогает при поиске каждой переменной iirc.
zzzzBov
2
@zzzzBov неверно. Удаление оченьwith помогает, поскольку позволяет браузеру понять, какое выражение переменной относится к какой переменной.
John Dvorak
2
Мне кажется неинтуитивным, что не-объект this«строже», чем всегда-объект this.
IllidanS4 хочет вернуть Монику
2
@ IllidanS4: Это в основном касается случаев, когда thisis nullили undefined, который будет глобальным объектом в небрежном режиме.
Bergi
6
@ IllidanS4: Думайте об этом как о «фактическом this» против «обертки this», если хотите. Обертки объектов - это кладж, которого никогда не должно было существовать, поэтому имеет смысл, чтобы строгий режим избегал их больше, когда это возможно.
Ry-