2012-03-19 38 views
21

Tôi đang xem http://www.youtube.com/watch?v=mHtdZgou0qU và vào khoảng 13:37 (hehe), anh ấy hiển thị một danh sách các mục cần tránh do việc thêm một đối tượng mới vào chuỗi phạm vi.Javascript - Sử dụng đóng cửa một cách tiết kiệm?

Tôi hiểu những gì anh ấy nói với các tuyên bố usingtry-catch, cũng như truy cập các biến ngoài phạm vi, nhưng tôi không hiểu tại sao phải đóng cửa. Nếu các biến cục bộ của kết thúc sẽ nằm trên đỉnh của chuỗi phạm vi, thì sự mất hiệu suất ở đâu?

+2

Chỉ cần nhớ: tối ưu hóa sớm là gốc rễ của mọi điều ác. Video đang thảo luận làm cho JavaScript hoạt động hiệu quả hơn, tuy nhiên hầu hết JavaScript không bao giờ cần phải được tối ưu hóa. Nếu bạn có cuộc gọi đến một chức năng được thiết lập không đồng bộ sau khi người dùng nhấp vào nút, người dùng sẽ không bao giờ nhận thấy sự khác biệt giữa tỷ lệ phản hồi 30ms và tỷ lệ phản hồi 40ms. – zzzzBov

+1

Các câu trả lời ở đây đều tốt. Tuy nhiên, hãy nhớ rằng video này đã được một vài năm và có thể không liên quan như t một lần. –

Trả lời

20

Đó là vì, để tra cứu các biến không phải là cục bộ, VM phải đi lên chuỗi phạm vi để tìm chúng. Mặt khác, các biến cục bộ được lưu trong bộ nhớ cache, vì vậy việc tra cứu biến cục bộ nhanh hơn nhiều. Hàm lồng nhau càng nhiều thì chuỗi phạm vi càng dài và tác động hiệu suất tiềm năng càng tăng.

Đây là một phần lý do tại sao bạn thường thấy mã như thế này trong các thư viện phổ biến JS:

(function(window, document, undefined) { 
    // ... 
})(window, document); 

Ở đây, windowdocument biến địa phương trở thành, vì vậy tìm chúng trở nên nhanh hơn nhiều, mà trở nên khá đáng chú ý nếu bạn tham khảo các đối tượng này hàng ngàn lần từ bên trong mã của bạn.

This page có mô tả chi tiết về chuỗi phạm vi và bối cảnh thực thi. (Toàn bộ bài viết thú vị nếu bạn có thời gian để đọc nó.)

Nhìn chung, các trình duyệt hiện đại tối ưu hóa tất cả các công cụ này đến mức không đáng kể.

+0

Điều này đúng, đó là vì các cửa sổ đóng cửa lưu trữ các tham chiếu đến các biến cục bộ chứ không phải là các bản sao, do đó nó buộc phải đi bộ xếp chồng thay vì một tra cứu cục bộ. –

+0

Tôi có nghĩa là đóng cửa không truy cập các biến ngoài phạm vi. Nó có vẻ hơi dư thừa để anh ta nói để tránh truy cập các biến ngoài phạm vi, và sau đó nói rõ ràng là tránh đóng cửa. Nó có vẻ ngụ ý rằng ông có nghĩa là ngoài việc sử dụng các biến địa phương, bạn cũng nên tránh đóng cửa. Tôi có hiểu sai những gì anh ta nói không? – mowwwalker

+2

@Walkerneo: Nó không phải là đóng cửa nếu bạn không tham chiếu các biến không phải cục bộ. Những gì anh ta nói là bạn nên tránh đóng cửa bất cứ khi nào có thể, nhưng khi bạn * có * tham chiếu biến ngoài phạm vi, bạn nên sử dụng biến cục bộ để thực hiện tra cứu nhanh hơn (nếu bạn sử dụng biến thường từ bên trong hàm đó- -Vâng, nó sẽ không làm được gì nhiều). –

9

The linked video explains why closure can inflict some performance hits starting about 11:08.

Về cơ bản, anh ấy nói rằng đối với mỗi chức năng lồng nhau, nó bổ sung thêm đối tượng khác để chuỗi phạm vi và do đó việc tiếp cận các biến bên ngoài của việc đóng cửa sẽ mất ngay cả lâu.

Để tìm giá trị liên quan đến một biến, các interprer Javascript sau quá trình này:

  1. tìm kiếm các đối tượng phạm vi địa phương
  2. nếu 1 không hiệu quả, tìm kiếm các đối tượng phạm vi mẹ
  3. nếu 2 không hoạt động, tìm đối tượng phạm vi chính của cha mẹ
  4. tiếp tục tìm kiếm phạm vi gốc cho đến
  5. bạn tìm kiếm phạm vi toàn cầu
  6. và nếu vẫn không tìm thấy, hãy ném một lỗi biến không xác định.

Trong một chức năng bình thường, để tìm biến, bạn chỉ phải tìm kiếm ở đầu chuỗi phạm vi. Một đóng cửa, mặt khác, để tìm các biến cha mẹ sẽ phải tìm kiếm xuống chuỗi phạm vi, đôi khi nhiều cấp độ sâu. Ví dụ, nếu bạn đã có một số đóng cửa như thế này (lưu ý rằng đây là một rất giả tạo ví dụ):

function a (x) { 
    function b (y) { 
    return (function (z) { 
     return x + y + z; 
    })(y + y); 
    } 
    return b(x + 3); 
} 

Từ chức năng trong cùng, để đánh giá sự biểu hiện x + y + z, nó phải đi qua lên ba cấp độ trong phạm vi chuỗi để tìm x, sau đó nó phải đi qua chuỗi phạm vi một lần nữa hai cấp độ để tìm y, và sau đó cuối cùng một lần để tìm z.Tổng cộng, nó phải tìm kiếm sáu đối tượng trong chuỗi phạm vi để trả lại kết quả cuối cùng.

Điều này là không thể tránh khỏi khi đóng cửa, vì các đóng bao luôn phải truy cập vào các biến mẹ. Nếu họ không ở đó sẽ không có mục đích trong việc sử dụng một đóng cửa.

Cũng lưu ý rằng trong Javascript, có một chi phí đáng kể trong việc tạo ra các chức năng, đặc biệt là đóng cửa. Lấy ví dụ, đóng cửa khá đơn giản này:

function a(x) { 
    return function (y) { 
    return x + y; 
    } 
} 

Và bạn gọi nó là thời gian khác nhau, như thế này

var x = a(1); 
var y = a(2); 
var z = a(3); 
alert(x(3)); // 4 
alert(y(3)); // 5 
alert(z(3)); // 6 

Bạn sẽ nhận thấy rằng chức năng trở về từ a có để giữ những gì đã được thông qua như một đối số trong hàm cha ngay cả sau khi hàm cha đã được gọi. Điều này có nghĩa là người thông dịch phải ghi nhớ những gì bạn đã truyền vào cho mọi chức năng mà bạn đã gọi cho đến giờ.

+0

Lưu trữ 'y' và' z' làm biến cục bộ trong hàm thứ ba vẫn giảm hiệu năng? Tất nhiên, đối với ví dụ này, không có lý do gì để tạo bao đóng, nhưng còn khi nào thì có? – mowwwalker

+0

@Walkerneo Nó sẽ vẫn phải đi lên chuỗi phạm vi để tìm các biến mẹ. Nếu bạn sử dụng các biến cha mẹ nhiều hơn một lần trong hàm bên trong, nó sẽ có giá trị khai báo dưới dạng biến cục bộ, nhưng nếu bạn chỉ sử dụng chúng một lần, thì không có gì hữu ích về việc khai báo chúng dưới dạng biến cục bộ. –

+0

Được rồi, tôi hiểu vấn đề với việc vượt qua chuỗi phạm vi, nhưng, như tôi đã nói trong nhận xét của tôi về câu trả lời khác, cách anh ta đặt "sử dụng đóng cửa một cách tiết kiệm" ngay sau những gì anh ta nói về các biến ngoài phạm vi. giống như có một cái gì đó vốn đã sai với bao đóng và không chỉ là họ thêm một đối tượng khác vào chuỗi phạm vi. – mowwwalker

Các vấn đề liên quan