Mengapa "gunakan ketat" meningkatkan kinerja 10x dalam contoh ini?

128

Berikut pertanyaan Memperluas kinerja String.prototype saya benar-benar tertarik, karena hanya menambahkan "use strict"ke String.prototypekinerja metode ditingkatkan 10 kali. The penjelasan oleh bergi pendek dan tidak menjelaskan kepada saya. Mengapa ada perbedaan dramatis antara dua metode yang hampir identik, yang hanya berbeda di "use strict"atas? Bisakah Anda menjelaskan lebih detail dan dengan teori di balik ini?

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

Hasil:

proto: 101 ms
proto-strict: 7.5 ms
exebook
sumber
1
Bisakah Anda melakukan tes dengan this[i] === chardan melihat apakah Anda mendapatkan perbedaan yang sama?
Niet the Dark Absol
1
Saya diuji dengan this[i] === chardi lingkungan DOM dan hasilnya sama
Cristian Traìna
2
Penjelasan bergi mengatakan bahwa ketika Anda memanggil countfungsi, thisparameter harus dilemparkan ke objek string bukan string literal sedangkan dalam mode ketat tidak harus untuk beroperasi dengan benar. Mengapa hal ini terjadi di luar jangkauan saya, saya sangat tertarik dengan jawabannya.
Nick Larsen
3
@NickLarsen: Hanya bagaimana bahasanya ditentukan. Secara tradisional JS akan memastikan Anda selalu memiliki objek this, tetapi dalam mode ketat itu melompati langkah itu, sehingga Anda mendapatkan string primitif , atau apa pun yang disediakan untuk itu this.
6
Sudah waktunya untuk menempatkan di "use strict";mana - mana anak laki-laki! Goooold
Jonathan

Jawaban:

155

Dalam mode ketat, thiskonteksnya tidak dipaksa menjadi objek. Jika Anda memanggil suatu fungsi pada non-objek,this hanya akan non-objek itu.

Sebaliknya, dalam mode non-ketat, thiskonteksnya selalu dibungkus pertama dalam objek jika belum menjadi objek. Misalnya, (42).toString()membungkus pertama 42dalam Numberobjek dan kemudian memanggil Number.prototype.toStringdengan Numberobjek sebagai thiskonteks. Dalam mode ketat, thiskonteksnya tidak tersentuh dan hanya memanggil Number.prototype.toStringdengan 42sebagai thiskonteks.

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

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

Dalam kasus Anda, versi mode non-ketat menghabiskan banyak waktu membungkus dan membuka bungkus primitif stringke dalam Stringpembungkus objek dan kembali. Versi mode ketat di sisi lain langsung bekerja pada primitif string, yang meningkatkan kinerja.

Mattias Buelens
sumber
1
Dan penghapusan withjuga membantu sedikit untuk setiap pencarian variabel iirc.
zzzzBov
2
@zzzzBov salah. Penghapusan sangatwith membantu karena memungkinkan browser untuk alasan ekspresi variabel mana yang merujuk ke variabel mana.
John Dvorak
2
Tampaknya tidak intuitif bagi saya bahwa non-objek thislebih "ketat" daripada selalu-objek this.
IllidanS4 ingin Monica kembali
2
@ IllidanS4: Ini adalah sebagian besar tentang kasus di mana thisadalah nullatau undefined, yang akan menjadi obyek global dalam mode ceroboh.
Bergi
6
@ IllidanS4: Anggap saja sebagai "aktual this" vs. "bungkus this" jika Anda mau. Pembungkus objek adalah kludge yang seharusnya tidak pernah ada, jadi masuk akal bahwa mode ketat akan menghindarinya lebih banyak bila memungkinkan.
Ry-