Tại sao sử dụng nghiêm ngặt, cải thiện hiệu suất 10 lần trong ví dụ này?


128

Sau câu hỏi Mở rộng hiệu suất String.prototype tôi thực sự bị thu hút, bởi vì chỉ cần thêm "use strict"vào một String.prototypephương thức đã cải thiện hiệu suất 10 lần. Lời giải thích của bergi rất ngắn gọn và không giải thích cho tôi. Tại sao có sự khác biệt lớn như vậy giữa hai phương pháp gần như giống hệt nhau, chỉ khác nhau ở "use strict"đầu? Bạn có thể giải thích chi tiết hơn và với lý thuyết đằng sau điều này?

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

Kết quả:

proto: 101 ms
proto-strict: 7.5 ms

1
Bạn có thể làm một bài kiểm tra với this[i] === charvà xem nếu bạn nhận được sự khác biệt tương tự?
Niet the Dark Tuyệt đối

1
Tôi đã thử nghiệm this[i] === chartrong môi trường DOM và kết quả là như nhau
Cristian Traìna 16/07/2016

2
lời giải thích của bergi nói rằng khi bạn gọi counthàm, thistham số phải được truyền tới một đối tượng chuỗi thay vì chuỗi ký tự trong khi ở chế độ nghiêm ngặt, nó không phải hoạt động chính xác. Tại sao đây là trường hợp nằm ngoài tôi, tôi rất quan tâm đến câu trả lời.
Nick Larsen

3
@NickLarsen: Đó là cách ngôn ngữ được chỉ định. Theo truyền thống, JS sẽ đảm bảo bạn luôn có một đối tượng this, nhưng trong chế độ nghiêm ngặt, nó bỏ qua bước đó, do đó bạn có được chuỗi nguyên thủy hoặc bất cứ thứ gì được cung cấp cho this.

6
Đã đến lúc đặt "use strict";con trai khắp nơi! Goooold
Jonathan

Câu trả lời:


155

Trong chế độ nghiêm ngặt, thisbối cảnh không bị buộc phải là một đối tượng. Nếu bạn gọi một hàm trên một đối tượng, thissẽ không phải là đối tượng đó.

Ngược lại, ở chế độ không nghiêm ngặt, thisbối cảnh luôn được bao bọc đầu tiên trong một đối tượng nếu nó chưa phải là một đối tượng. Ví dụ, (42).toString()đầu tiên kết thúc tốt đẹp 42trong một Numberđối tượng và sau đó gọi Number.prototype.toStringvới Numberđối tượng là thisbối cảnh. Trong chế độ nghiêm ngặt, các thisbối cảnh còn lại các cuộc gọi bị ảnh hưởng và chỉ Number.prototype.toStringvới 42như thisbối cảnh.

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

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

Trong trường hợp của bạn, phiên bản chế độ không nghiêm ngặt dành nhiều thời gian để bọc và tháo các strings nguyên thủy vào các Stringhàm bao đối tượng và quay lại. Phiên bản chế độ nghiêm ngặt mặt khác hoạt động trực tiếp trên nguyên thủy string, giúp cải thiện hiệu suất.


1
Và việc loại bỏ withcũng giúp một chút cho mỗi iirc tra cứu biến.
zzzzBov 16/07/2016

2
@zzzzBov không chính xác. Việc loại bỏ withtrợ giúp vô cùng vì nó cho phép trình duyệt suy ra biểu thức biến nào đề cập đến biến nào.
John Dvorak

2
Có vẻ không trực quan với tôi rằng phi đối tượng this"chặt chẽ" hơn đối tượng luôn luôn this.
IllidanS4 muốn Monica trở lại vào

2
@ IllidanS4: Đây là chủ yếu về trường hợp thisnullhay undefined, đó sẽ là đối tượng toàn cầu trong chế độ cẩu thả.
Bergi

6
@ IllidanS4: Hãy nghĩ về nó như "thực tế this" so với "trình bao bọc this" nếu bạn thích. Các hàm bao đối tượng là một loại bùn không bao giờ tồn tại, do đó, có ý nghĩa rằng chế độ nghiêm ngặt sẽ tránh chúng nhiều hơn khi có thể.
Ry-
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.