2009-02-14 38 views
15

thể trùng lặp:
Can a JavaScript object have a prototype chain, but also be a function?Làm cách nào để tạo một đối tượng JS có thể gọi với một mẫu thử tùy ý?

tôi đang tìm cách để làm cho một đối tượng JavaScript callable, với một chuỗi nguyên mẫu tùy ý, nhưng mà không sửa đổi Function.prototype.

Nói cách khác, điều này có làm việc:

var o = { x: 5 }; 
var foo = bar(o); 
assert(foo() === "Hello World!"); 
delete foo.x; 
assert(foo.x === 5); 

Nếu không thực hiện bất kỳ thay đổi trên toàn cầu.

+0

Cơ chế cơ bản là giống nhau, nhưng câu hỏi này hơi khác (ví dụ điểm bắt đầu khác hoàn toàn) và câu trả lời thêm giá trị đáng kể. Được đề cử để mở lại. – kanaka

Trả lời

12

Không có gì ngăn bạn thêm các thuộc tính tùy ý vào một hàm, ví dụ:

function bar(o) { 
    var f = function() { return "Hello World!"; } 
    o.__proto__ = f.__proto__; 
    f.__proto__ = o; 
    return f; 
} 

var o = { x: 5 }; 
var foo = bar(o); 
assert(foo() === "Hello World!"); 
delete foo.x; 
assert(foo.x === 5); 

Tôi tin rằng nên làm những gì bạn muốn.

này hoạt động bằng cách tiêm các đối tượng o vào chuỗi nguyên mẫu, tuy nhiên có một vài điều cần lưu ý:

  • Tôi không biết nếu IE hỗ trợ __proto__, hoặc thậm chí có một tương đương, Frome của một số bình luận điều này có vẻ chỉ hoạt động trong các trình duyệt dựa trên firefox và safari (vì vậy camino, chrome, vv cũng hoạt động).
  • o.__proto__ = f.__proto__; chỉ thực sự cần thiết đối với các hàm mẫu thử chức năng như function.toString, vì vậy bạn có thể chỉ muốn bỏ qua nó, đặc biệt nếu bạn mong đợi o để có mẫu thử có ý nghĩa.
+0

Bạn có một! sau khi chào thế giới trong khẳng định, vì vậy nó là sai;) – some

+1

Không hoạt động trong IE hoặc Opera 9.61 – some

+0

Bah, tôi nên đã thử nghiệm, nhưng tôi chỉ sao chép khẳng định từ câu hỏi gốc – olliej

1

Điều duyệt chéo gần nhất tôi đã đi là thế này (thử nghiệm trong FF, IE, Crome và Opera):

function create(fun,proto){ 
    var f=function(){}; 
    //Copy the object since it is going to be changed. 
    for (var x in proto) 
     f.prototype[x] = proto[x]; 
    f.prototype.toString = fun; 
    return new f; 
} 
var fun=function(){return "Hello world";} 
var obj={x:5} 

var foo=create(fun,obj); 
foo.x=8; 
alert(foo); //Hello world 
alert(foo.x); // 8 
delete foo.x; 
alert(foo.x); // 5 
+0

Thật không may tôi nghĩ rằng sẽ được sửa đổi nguyên mẫu chức năng toàn cầu: -/ – olliej

+0

@Olliej: Tại sao? Tôi đang tạo một hàm trống mới và sửa đổi nguyên mẫu của nó, không phải nguyên mẫu hàm toàn cầu. – some

+0

Ah, vâng, bạn nói đúng, cái xấu của tôi - tuy nhiên mới f đang tạo ra một đối tượng, không phải là một hàm có thể được gọi. ví dụ: tạo (fun, obj)() sẽ không làm những gì được hỏi. Tôi nghi ngờ câu hỏi yêu cầu không thể thực hiện được trong IE/Opera. – olliej

3

tôi đang tìm cách để làm cho một đối tượng JavaScript callable, với chuỗi nguyên mẫu tùy ý, nhưng không sửa đổi Function.prototype.

Tôi không nghĩ rằng có một cách cầm tay để làm điều này:

Bạn phải thể thiết lập [[Prototype]] sở hữu một hàm của đối tượng hoặc thêm một [[Gọi]] tài sản cho một đối tượng thông thường. Việc đầu tiên có thể được thực hiện thông qua các tài sản phi tiêu chuẩn __proto__ (xem olliej's answer), thứ hai là không thể theo như tôi biết.

[[Prototype]] chỉ có thể được đặt trong khi tạo đối tượng thông qua thuộc tính prototype của hàm tạo. Thật không may, theo như tôi biết không có triển khai JavaScript nào sẽ cho phép tạm thời gán lại Function.prototype.

0

Bạn không thể thực hiện theo cách di động. Tuy nhiên, nếu bạn nghĩ về nó, nếu mục đích của delete foo.x; là đặt lại giá trị x, bạn có thể cung cấp phương thức reset() trên foo sẽ khôi phục thuộc tính bị thiếu về giá trị mặc định của chúng.

// Object creation and initialisation 
(foo=function() 
{ 
    alert("called"); 
}).reset = function() 
{ 
    if(!("x"in this)) this.x=42; 
}; 
foo.reset(); 

// Using our callable object 
          alert(foo.x); // 42 
foo();      alert(foo.x); // called; 42 
foo.x=3;  foo.reset(); alert(foo.x); // 3 [*] 
delete foo.x; foo.reset(); alert(foo.x); // 42 

(Thử nghiệm trong Chromium và Internet Explorer, nhưng điều này sẽ hoạt động trong tất cả các trình duyệt.)

Trong dòng được đánh dấu bằng [*] gọi là reset thực sự là không cần thiết, nhưng nó ở đó để chứng minh rằng không quan trọng nếu bạn gọi nó là vô tình, và điều này tổng quát đến nhiều hơn một thuộc tính một cách dễ dàng.

Lưu ý rằng trong phần thân hàm của đối tượng có thể gọi this sẽ tham chiếu đến phạm vi chứa, có thể sẽ không hữu ích cho chúng tôi vì chúng tôi muốn cơ quan chức năng truy cập các thành viên đối tượng. Để giảm thiểu điều này, hãy quấn nó trong một bao đóng như thế này:

foo = (function() 
{ 
    var self = function() 
    { 
     self.x = 42; 
    }; 
    return self; 
})(); 
foo(); alert(foo.x); // 42 
Các vấn đề liên quan