Tại sao Array = [] nhanh hơn Array = new Array?


146

Tôi đã chạy mã này và nhận được kết quả dưới đây. Tôi tò mò muốn biết tại sao []nhanh hơn?

console.time('using[]')
for(var i=0; i<200000; i++){var arr = []};
console.timeEnd('using[]')

console.time('using new')
for(var i=0; i<200000; i++){var arr = new Array};
console.timeEnd('using new')
  • sử dụng []: 299ms
  • sử dụng new: 363ms

Nhờ Raynos, đây là điểm chuẩn của mã này và một số cách khả dĩ hơn để xác định một biến.

nhập mô tả hình ảnh ở đây


5
Bạn có thể quan tâm đến jsperf .
Mũi nhọn


Lưu ý từ khóa mới. Điều này có nghĩa là "làm ơn kém hiệu quả hơn". Nó không bao giờ có ý nghĩa và yêu cầu trình duyệt thực hiện khởi tạo bình thường thay vì cố gắng tối ưu hóa.
beatgammit

2
@kinakuta không. Cả hai đều tạo ra các đối tượng không bằng nhau mới. Ý tôi []là tương đương với new Array()mã nguồn, không phải các đối tượng trả về biểu thức biểu mẫu
Raynos

1
Vâng, nó không quan trọng lắm. Nhưng tôi muốn biết.
Mohsen

Câu trả lời:


195

Mở rộng hơn nữa về các câu trả lời trước ...

Từ góc độ trình biên dịch chung và bỏ qua các tối ưu hóa dành riêng cho VM:

Đầu tiên, chúng ta trải qua giai đoạn phân tích từ vựng, nơi chúng ta mã hóa mã.

Bằng cách ví dụ, các mã thông báo sau có thể được tạo ra:

[]: ARRAY_INIT
[1]: ARRAY_INIT (NUMBER)
[1, foo]: ARRAY_INIT (NUMBER, IDENTIFIER)
new Array: NEW, IDENTIFIER
new Array(): NEW, IDENTIFIER, CALL
new Array(5): NEW, IDENTIFIER, CALL (NUMBER)
new Array(5,4): NEW, IDENTIFIER, CALL (NUMBER, NUMBER)
new Array(5, foo): NEW, IDENTIFIER, CALL (NUMBER, IDENTIFIER)

Hy vọng rằng điều này sẽ cung cấp cho bạn một hình ảnh đầy đủ để bạn có thể hiểu được cần phải xử lý nhiều hơn (hoặc ít hơn).

  1. Dựa trên các mã thông báo ở trên, chúng tôi biết rằng thực tế ARRAY_INIT sẽ luôn tạo ra một mảng. Do đó, chúng tôi chỉ đơn giản là tạo ra một mảng và điền vào nó. Theo như sự mơ hồ, giai đoạn phân tích từ vựng đã phân biệt ARRAY_INIT với một trình truy cập thuộc tính đối tượng (ví dụ obj[foo]) hoặc dấu ngoặc trong chuỗi / regex bằng chữ (ví dụ: "foo [] bar" hoặc / [] /)

  2. Đây là rất nhỏ, nhưng chúng tôi cũng có nhiều mã thông báo hơn new Array. Hơn nữa, chúng ta không hoàn toàn rõ ràng rằng chúng ta chỉ muốn tạo một mảng. Chúng tôi thấy mã thông báo "mới", nhưng "mới" là gì? Sau đó, chúng tôi thấy mã thông báo IDENTIFIER có nghĩa là chúng tôi muốn có một "Mảng" mới, nhưng nhìn chung JavaScript VM không phân biệt mã thông báo IDENTIFIER và mã thông báo cho "các đối tượng toàn cầu gốc". Vì thế...

  3. Chúng tôi phải tìm kiếm chuỗi phạm vi mỗi lần chúng tôi gặp mã thông báo IDENTIFIER. Máy ảo Javascript chứa "Đối tượng kích hoạt" cho từng bối cảnh thực thi có thể chứa đối tượng "đối số", biến được xác định cục bộ, v.v. Nếu chúng ta không thể tìm thấy nó trong đối tượng Kích hoạt, chúng ta bắt đầu tìm kiếm chuỗi phạm vi cho đến khi chúng ta đạt đến phạm vi toàn cầu . Nếu không có gì được tìm thấy, chúng tôi ném a ReferenceError.

  4. Khi chúng ta đã định vị khai báo biến, chúng ta gọi hàm tạo. new Arraylà một lệnh gọi hàm ẩn và quy tắc chung là các lệnh gọi hàm chậm hơn trong khi thực thi (do đó tại sao trình biên dịch C / C ++ tĩnh cho phép "hàm nội tuyến" - điều mà các công cụ JS JIT như SpiderMonkey phải thực hiện khi đang di chuyển)

  5. Các Arrayconstructor bị quá tải. Hàm tạo Array được triển khai dưới dạng mã gốc, do đó nó cung cấp một số cải tiến hiệu năng, nhưng nó vẫn cần kiểm tra độ dài đối số và hành động tương ứng. Hơn nữa, trong trường hợp chỉ có một đối số được cung cấp, chúng ta cần kiểm tra thêm loại đối số. Mảng mới ("foo") tạo ra ["foo"] trong đó Mảng mới (1) tạo ra [không xác định]

Vì vậy, để đơn giản hóa tất cả: với các mảng bằng chữ, VM biết chúng ta muốn một mảng; với new Array, VM cần sử dụng các chu kỳ CPU bổ sung để tìm ra những gì new Array thực sự làm.


không phải là a = new Array (1000); for (từ 0 đến 999) {a [i] = i} nhanh hơn a = []; for (từ 0 đến 999) {a [i] = i} vì phân bổ chi phí mặc dù?
Y. Yoshii

Chỉ cần làm một trường hợp thử nghiệm. new Array (n) nhanh hơn trong trường hợp bạn biết kích thước của mảng trước thời hạn jsperf.com/sapes-braces-vs-new-array
Y. Yoshii

27

Một lý do có thể là new Arrayyêu cầu tra cứu tên Array(bạn có thể có một biến với tên đó trong phạm vi), trong khi []không.


4
Kiểm tra đối số cũng có thể đóng góp.
Leonid

Arraychấp nhận cả một đối số lenvà nhiều đối số. Trường hợp như []chỉ chấp nhận nhiều đối số. Ngoài ra các thử nghiệm firefox cho thấy hầu như không có sự khác biệt.
Raynos

Tôi nghĩ rằng có một số sự thật về điều đó. Chạy thử nghiệm vòng lặp của OP trong IIFE tạo ra tác động (tương đối) đáng kể đến hiệu suất. Bao gồm var Array = window.Arraycải thiện hiệu suất của new Arraybài kiểm tra.
dùng113716

Tôi không nghĩ điều đó đúng bởi vì console.time này ('nhiều vars mới'); for (var i = 0; i <200000; i ++) {var Array = new Array ()}; console.timeEnd ('nhiều vars mới'); nhiều vars mới hơn: 390ms và console.time này ('vars new'); var myOtherObject = {}, myOtherArray = []; for (var i = 0; i <200000; i ++) {var Array = new Array ()}; console.timeEnd ('nhiều vars mới'); nhiều vars mới hơn: 369ms Trả về cùng một lúc
Mohsen

2

Câu hỏi hay. Ví dụ đầu tiên được gọi là một mảng bằng chữ. Đó là cách ưa thích để tạo mảng giữa nhiều nhà phát triển. Có thể là sự khác biệt hiệu năng được gây ra bằng cách kiểm tra các đối số của lệnh gọi Array () mới và sau đó tạo đối tượng, trong khi nghĩa đen tạo ra một mảng trực tiếp.

Sự khác biệt tương đối nhỏ trong hiệu suất hỗ trợ điểm này tôi nghĩ. Bạn có thể thực hiện cùng một bài kiểm tra với Đối tượng và đối tượng bằng chữ {}.


1

Điều này sẽ có ý nghĩa

Đối tượng bằng chữ cho phép chúng tôi viết mã hỗ trợ nhiều tính năng nhưng vẫn khiến nó trở nên tương đối đơn giản đối với những người triển khai mã của chúng tôi. Không cần phải gọi trực tiếp các hàm tạo hoặc duy trì thứ tự đúng của các đối số được truyền cho các hàm, v.v.

http://www.dyn-web.com/tutorials/obj_lit.php


1

Ngoài ra, điều thú vị là, nếu biết trước độ dài của mảng (các yếu tố sẽ được thêm ngay sau khi tạo), việc sử dụng một hàm tạo mảngđộ dài được chỉ định sẽ nhanh hơn nhiều trên Google Chrome 70+ gần đây.

  • " Mảng mới ( % ARR_LENGTH% ) " - 100% (nhanh hơn) !

  • " [] " - 160-170% (chậm hơn)

Biểu đồ với kết quả của các biện pháp.

Bài kiểm tra có thể được tìm thấy ở đây - https://jsperf.com/small-arr-init-with- Unknown - length - bretsets - vs - new - array/2

Lưu ý: kết quả này đã được thử nghiệm trên Google Chrome v, 70 ; trong Firefox v70 và IE cả hai biến thể gần như bằng nhau.

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.