2010-07-18 33 views
6

Tôi đã làm việc trên một dự án trong một thời gian, cố gắng tìm ra những gì tôi đã làm sai, khi cuối cùng tôi thu hẹp "lỗi" xuống thực tế là mã bên dưới không hoạt động như tôi mong đợi:'này' trong các chức năng JavaScript riêng tư mà không cần hack?

function Alpha() 
    { 
    this.onion = 'onion'; 

    function Beta() 
     { 
     alert(this.onion); 
     } 

    Beta(); 
    } 

alpha1 = new Alpha(); 
// Alerts 'undefined' 

Tuy nhiên, nếu tôi thay đổi mã này:

function Alpha() 
    { 
    var self = this; 
    this.onion = 'onion'; 

    function Beta() 
     { 
     alert(self.onion); 
     } 

    Beta(); 
    } 

alpha1 = new Alpha(); 
// Alerts 'onion' 

nó hoạt động như tôi mong đợi. Sau khi lãng phí một phần lớn cuộc sống của tôi, bất cứ ai có thể giải thích lý do tại sao nó hoạt động như thế này?

Trả lời

11

Làm việc như thế này bởi vì mỗi hàm đã liên kết ngữ cảnh thực thi riêng của mình.

Tuy nhiên có những cách khác để làm điều đó, ví dụ:

Sử dụng call hoặc apply để gọi hàm:

function Alpha() { 
    this.onion = 'onion'; 

    function Beta() { 
    alert(this.onion); 
    } 

    Beta.call(this); 
} 

var alpha1 = new Alpha(); 
// Alerts 'onion' 

mới ECMAScript 5 Standard Edition, giới thiệu một cách để kéo dài chức năng bối cảnh, các Function.prototype.bind phương pháp:

function Alpha() { 
    this.onion = 'onion'; 

    var Beta = function() { 
    alert(this.onion); 
    }.bind(this); 

    Beta(); 
} 

var alpha1 = new Alpha(); 
// Alerts 'onion' 

Có thể nói rằng hàm Betabị ràng buộc và bất kể bạn gọi nó như thế nào, giá trị this của nó sẽ là nguyên vẹn.

Phương pháp này chưa được hỗ trợ rộng rãi, hiện tại chỉ có IE9pre3 bao gồm nó, nhưng bạn có thể bao gồm an implementation để làm cho nó hoạt động ngay bây giờ.

Bây giờ hãy để tôi xây dựng về cách this công trình:

Giá trị this tồn tại trên mỗi execution context, và cho Mã hàm được thiết lập ngầm khi một cuộc gọi chức năng được thực hiện, giá trị được xác định tùy thuộc cách tham khảo nếu hình thành.

Trong ví dụ của bạn, khi bạn gọi Beta();, vì nó không bị ràng buộc với bất kỳ đối tượng, chúng ta có thể nói rằng các tài liệu tham khảo không có đối tượng cơ sở, sau đó, giá trị this sẽ đề cập đến đối tượng toàn cầu.

trường hợp khác xảy ra khi bạn gọi một chức năng đó là ràng buộc như một thuộc tính của một đối tượng, ví dụ:

var obj = { 
    foo: function() { return this === obj;} 
}; 
obj.foo(); // true 

Như bạn có thể thấy, các tài liệu tham khảo được gọi obj.bar(); chứa một đối tượng cơ sở, đó là obj và giá trị this bên trong hàm được gọi sẽ tham chiếu đến nó.

Lưu ý: reference type là khái niệm trừu tượng, được xác định cho mục đích triển khai ngôn ngữ bạn có thể xem chi tiết trong thông số.

Một trường hợp thứ ba trong đó giá trị this được thiết lập ngầm là khi bạn sử dụng các nhà điều hành new, nó sẽ đề cập đến một đối tượng mới được tạo ra mà được thừa hưởng từ nguyên mẫu constructor của nó:

function Foo() { 
    return this; // `this` is implicitly returned when a function is called 
}    // with `new`, this line is included only to make it obvious 

var foo = new Foo(); 
foo instanceof Foo; // true 
Foo.prototype.isPrototypeOf(foo); // true 
+0

+1. Tràn hơn bao giờ hết. –

+0

Cảm ơn bạn @Tim! – CMS

2

Khi bạn gọi chức năng không phải là thành viên (không được gọi là someObject.method()), nó chạy trong ngữ cảnh của cửa sổ. Nó không quan trọng cho dù đó là một chức năng riêng hoặc một chức năng toàn cầu.

Bạn có thể làm:

call cho phép bạn gọi một hàm trong khi đi qua một bối cảnh như là đối số đầu tiên (apply là tương tự, nhưng danh sách đối số là một mảng).

Tuy nhiên, tôi không rõ ràng (ngay cả đối với ví dụ tầm thường) tại sao hành tây là công khai nhưng Beta là riêng tư.

+0

nhưng nếu tôi đã làm: var Beta = function() {alert (self.onion);}, nó sẽ cho kết quả tương tự, mặc dù Beta là thành viên riêng tư. – Nick

+0

Đó vẫn chưa phải là chức năng của thành viên. Các hàm thành viên JavaScript là công khai. –

+0

Hmm .. okay, bị nhầm lẫn với thuật ngữ. Nhưng Beta là một thành viên riêng (có giá trị là một chức năng), chỉ cần không phải là một chức năng thành viên phải không? – Nick

8

Từ JavaScript: The Definitive Guide, 5th Edition (loài tê giác cuốn sách):

Khi một hàm được gọi là một hàm chứ không phải là một phương pháp, từ khóa this đề cập đến glo bal đối tượng. Gây nhầm lẫn, điều này đúng ngay cả khi một hàm lồng nhau được gọi (như một chức năng) trong vòng một phương pháp có chứa được gọi như một phương pháp: từ khóa this có một giá trị trong chứa chức năng nhưng (counterintuitively) đề cập đến đối tượng toàn cầu trong phần nội dung của hàm lồng nhau .

Lưu ý rằng this là từ khóa, không phải là tên biến hoặc tên thuộc tính . Cú pháp JavaScript không cho phép bạn chỉ định giá trị cho this.

Hai điều cần chú ý đến ở đây:

  1. this không phải là một biến, do đó, không áp dụng các quy tắc chụp đóng cửa bình thường.
  2. this là "hồi phục" trên mọi cuộc gọi functiion, cho dù là phương thức, cuộc gọi chức năng bình thường hoặc qua new. Vì bạn đang thực hiện một cuộc gọi chức năng bình thường (khi gọi Beta), this sẽ bị ràng buộc với "đối tượng chung".
Các vấn đề liên quan