2010-04-19 5 views
9

Tôi có một đối tượng singleton rằng sử dụng một đối tượng (không singleton), để đòi hỏi một số thông tin đến máy chủ:JavaScript 'lớp' và các vấn đề singleton

var singleton = (function(){ 

    /*_private properties*/ 
    var myRequestManager = new RequestManager(params, 
    //callbacks 
    function(){ 
     previewRender(response); 
    }, 
    function(){ 
     previewError(); 
    } 
); 

    /*_public methods*/ 
    return{ 

    /*make a request*/ 
    previewRequest: function(request){ 
     myRequestManager.require(request); //err:myRequestManager.require is not a func 
    }, 

    previewRender: function(response){ 
     //do something 
    }, 

    previewError: function(){ 
     //manage error 
    } 
    }; 
}()); 

Đây là 'lớp' khiến yêu cầu đến máy chủ

function RequestManager(params, success, error){ 
    //create an ajax manager 
    this.param = params; 
    this._success = success; //callbacks 
    this._error = error; 
} 

RequestManager.prototype = { 

    require: function(text){ 
    //make an ajax request 
    }, 
    otherFunc: function(){ 
    //do other things 
    } 

}

vấn đề là tôi không thể gọi myRequestManager.require từ bên trong đối tượng singleton. Firebug consolle nói: "myRequestManager.require không phải là một hàm", nhưng tôi không hiểu vấn đề ở đâu. Có giải pháp nào tốt hơn để thực hiện tình huống này không?

+0

Tôi không nhận được lỗi ... Các tham số 'đến từ đâu khi bạn xây dựng RequestManager? –

+0

Trong mẫu mã bạn cung cấp, thông số không được xác định. Bạn đã để lại một cái gì đó trong ví dụ của bạn? – Robusto

Trả lời

6

Mã của bạn theo thứ tự bạn đã trích dẫn, phải không? Singleton xuất hiện phía trên RequestManager trong nguồn?

Nếu có, đó là vấn đề của bạn. (!) Đó là khá tinh tế, nhưng giả sử hai bạn bit mã được niêm yết theo thứ tự bạn đã cho họ thấy, đây là thứ tự mà sự việc xảy ra (tôi sẽ giải thích nó dưới lên):

  1. Chức năng RequestManager được xác định.
  2. Chức năng ẩn danh của bạn tạo ra các lần chạy đơn lẻ của bạn, bao gồm việc khởi tạo phiên bản RequestManager.
  3. Nguyên mẫu RequestManager được thay thế bằng nguyên mẫu mới.

Kể từ khi dụ myRequestManager được instantiated trước nguyên mẫu đã được thay đổi, nó không có các chức năng bạn đã định nghĩa trên đó (mới) nguyên mẫu. Nó tiếp tục sử dụng đối tượng nguyên mẫu đã được đặt ra khi nó được khởi tạo.

Bạn có thể khắc phục điều này dễ dàng bằng cách sắp xếp lại mã, hoặc bằng cách thêm thuộc tính để RequestManager 's nguyên mẫu chứ không phải thay thế nó, ví dụ:

RequestManager.prototype.require = function(text){ 
    //make an ajax request 
}; 
RequestManager.prototype.otherFunc = function(){ 
    //do other things 
}; 

đó làm việc vì bạn chưa thay sự đối tượng nguyên mẫu, bạn vừa thêm vào nó. myRequestManager thấy các bổ sung bởi vì bạn đã thêm chúng vào đối tượng mà nó đang sử dụng (thay vì thiết lập một đối tượng mới trên hàm thuộc tính của hàm xây dựng là thuộc tính prototype).

Tại sao điều này xảy ra hơi kỹ thuật và tôi hầu như sẽ trì hoãn thông số kỹ thuật. Khi trình thông dịch nhập vào "ngữ cảnh thực thi" mới (ví dụ: một hàm hoặc toàn cảnh   — ví dụ, trang   — ngữ cảnh), thứ tự mà nó thực hiện không phải là thứ tự nguồn từ trên xuống nghiêm ngặt, có các giai đoạn . Một trong những giai đoạn đầu tiên là khởi tạo tất cả các hàm được định nghĩa trong ngữ cảnh; điều đó xảy ra trước khi bất kỳ mã từng bước nào được thực hiện. Chi tiết trong tất cả vinh quang của chúng trong phần 10.4.1 (mã toàn cầu), 10.4.3 (mã chức năng) và 10.5 (ràng buộc khai báo) trong the spec, nhưng về cơ bản, các hàm được tạo trước dòng đầu tiên của mã từng bước.:-)

Đây là đơn giản nhất để xem với một ví dụ thử nghiệm cách ly:

<!DOCTYPE HTML> 
<html> 
<head> 
<meta http-equiv="Content-type" content="text/html;charset=UTF-8"> 
<title>Test Page</title> 
<style type='text/css'> 
body { 
    font-family: sans-serif; 
} 
</style> 
<script type='text/javascript'> 
// Uses Thing1 
var User1 = (function() { 
    var thing1 = new Thing1(); 

    function useIt() { 
     alert(thing1.foo()); 
    } 

    return useIt; 
})(); 

// Uses Thing2 
var User2 = (function() { 
    var thing2 = new Thing2(); 

    function useIt() { 
     alert(thing2.foo()); 
    } 

    return useIt; 
})(); 

// Thing1 gets its prototype *replaced* 
function Thing1() { 
    this.name = "Thing1"; 
} 
Thing1.prototype = { 
    foo: function() { 
     return this.name; 
    } 
}; 

// Thing2 gets its prototype *augmented* 
function Thing2() { 
    this.name = "Thing2"; 
} 
Thing2.prototype.foo = function() { 
    return this.name; 
}; 

// Set up to use them 
window.onload = function() { 
    document.getElementById('btnGo').onclick = go; 
} 

// Test! 
function go() { 

    alert("About to use User1"); 
    try 
    { 
     User1(); 
    } 
    catch (e) 
    { 
     alert("Error with User1: " + (e.message ? e.message : String(e))); 
    } 

    alert("About to use User2"); 
    try 
    { 
     User2(); 
    } 
    catch (e) 
    { 
     alert("Error with User2: " + (e.message ? e.message : String(e))); 
    } 
} 

</script> 
</head> 
<body><div> 
<div id='log'></div> 
<input type='button' id='btnGo' value='Go'> 
</div></body> 
</html> 

Như bạn có thể thấy nếu bạn chạy nó, User1 thất bại vì Thing1 dụ nó đang sử dụng không có một tài sản foo (vì nguyên mẫu đã được thay thế), nhưng User2 hoạt động vì trường hợp Thing2 nó sử dụng * (vì nguyên mẫu được tăng cường, không được thay thế).

+1

+1 JavaScript tấn công các cú đánh bẫy một lần nữa! – bobince

+1

Cảm ơn lời giải thích và ví dụ, tôi đã không nhận được sự khác biệt giữa nguyên mẫu tăng cường và nguyên mẫu thay thế. Bây giờ nó hoạt động :-) –

+1

@Kucebe: Tuyệt vời! Vâng, đó là một cách tinh tế. :-) Không chỉ nguyên mẫu, đó là thứ tự mà trong đó mọi thứ xảy ra mà sẽ cắn bạn đôi khi (nó chắc chắn có * tôi *). –