2012-09-29 29 views
10

Làm cách nào để tạo hàm tạo cho đối tượng có thể gọi trong JavaScript?Constructor cho đối tượng có thể gọi trong JavaScript

Tôi đã thử nhiều cách khác nhau, như sau. Ví dụ có chỉ là ví dụ rút ngắn của đối tượng thực tế.

function CallablePoint(x, y) { 
    function point() { 
     // Complex calculations at this point 
     return point 
    } 
    point.x = x 
    point.y = y 
    return point 
} 

này hoạt động lúc đầu, nhưng các đối tượng nó tạo ra không phải là thể hiện của CallablePoint, vì vậy nó không sao chép các thuộc tính từ CallablePoint.prototype và nói false trên instanceof CallablePoint. Có thể làm cho constructor làm việc cho đối tượng có thể gọi được không?

+2

Bạn biết rằng 'điểm chức năng() {return point}' sẽ chỉ trả lại chính hàm đó? Bạn định làm gì với điều này? Nó sẽ cho phép bạn làm 'p()()()()()()()()()' nhưng điểm là gì? (không có ý định chơi chữ). –

+2

@FelixKling dễ hiểu tôi đã phải tự mình sử dụng lại để trả lời câu hỏi này – Pointy

+0

Vì mục đích hoàn chỉnh, ES6 có giải pháp tốt hơn cho vấn đề này, hãy xem: http://stackoverflow.com/questions/36871299/how-to-extend-function-with -es6-class – 0xc0de

Trả lời

19

Hóa ra nó thực sự có thể. Khi hàm được tạo, bằng cách sử dụng cú pháp function hoặc hàm tạo Function, hàm này sẽ nhận được thuộc tính nội bộ [[Call]]. Nó không phải là một đặc tính của hàm, mà là thuộc tính mà bất kỳ hàm nào được nhận khi được xây dựng.

Trong khi đó chỉ có nghĩa là bất cứ điều gì với [[Call]] có thể là chỉ Function khi nó được xây dựng (tốt, có một ngoại lệ – Function.prototype bản thân mà không kế thừa từ Function), điều đó không có nghĩa là nó không thể trở thành một cái gì đó khác sau này, trong khi bảo quản tài sản [[Call]]. Vâng, miễn là trình duyệt của bạn không phải là IE < 11.

Điều cho phép thay đổi phép thuật sẽ là __proto__ từ ES6, đã được triển khai trong nhiều trình duyệt. __proto__ là một tài sản huyền diệu có chứa nguyên mẫu hiện tại. Bằng cách thay đổi nó, tôi có thể tạo ra hàm kế thừa từ một thứ không phải là Function.

function CallablePoint(x, y) { 
    function point() { 
     // Complex calculations at this point 
     return point 
    } 
    point.__proto__ = CallablePoint.prototype 
    point.x = x 
    point.y = y 
    return point 
} 
// CallablePoint should inherit from Function, just so you could use 
// various function methods. This is not a requirement, but it's 
// useful. 
CallablePoint.prototype = Object.create(Function.prototype) 

Thứ nhất, các nhà xây dựng cho CallablePoint làm cho một Function (chỉ Function s được phép bắt đầu với [[Call]] tài sản. Tiếp theo, tôi thay đổi nguyên mẫu của nó, vì vậy nó sẽ kế thừa CallablePoint. Tại thời điểm này, tôi có một hàm điều đó không 't kế thừa từ Function (loại khó hiểu).

Sau khi tôi định nghĩa constructor cho CallablePoint s, tôi đặt nguyên mẫu của CallablePoint để Function, vì vậy tôi có CallablePoint được thừa kế từ Function.

01.

Bằng cách này, các trường hợp CallablePoint có chuỗi mẫu: CallablePoint -> Function -> Object, trong khi vẫn được gọi. Ngoài ra, vì đối tượng có thể gọi được, nó có theo đặc điểm kỹ thuật, typeof bằng 'function'.

-1

Nếu bạn muốn các nhà xây dựng CallablePoint() để trả lại một đối tượng kiểu CallablePoint, sau đó bạn có thể làm một cái gì đó như thế này mà các đối tượng CallablePoint chứa một điểm như một thuộc tính của đối tượng, nhưng vẫn là một đối tượng CallablePoint:

function CallablePoint(x, y) { 
    this.point = {}; 
    this.point.x = x 
    this.point.y = y 
} 

hoặc, nếu bạn đang thực sự cố gắng làm là để thực hiện một hàm trả về cho bạn một đối tượng của CallablePoint, sau đó bạn có thể tạo một hàm yếu tố:

function CallablePoint(x, y) { 
    this.point = {}; 
    this.point.x = x 
    this.point.y = y 
} 

function makeCallablePoint(x, y) { 
    return new CallablePoint(x,y); 
} 
+0

Tôi * nghĩ * mong muốn cho giá trị trả về là một hàm. Tôi không thể hiểu tại sao, tuy nhiên. – Pointy

+0

@Pointy - Tôi đồng ý rằng không rõ chính xác những gì OP đang cố gắng thực hiện. Tôi đã thêm một tùy chọn khác. – jfriend00

+2

'điểm.x' của bạn không phải là trang bạn vừa tạo trong 'this.point' – Alnitak

0

tôi không chắc chắn nếu bạn nhận thức được rằng đối tượng của bạn sẽ chỉ là một ví dụ của CallablePoint nếu bạn sử dụng từ khóa new. Bằng cách đặt tên cho nó là "có thể gọi", bạn khiến tôi nghĩ bạn không muốn để sử dụng new. Dù sao, có một cách để buộc một thể hiện để được trả lại (thanks for the tip, Resig):

function CallablePoint(x, y) { 
    if (this instanceof CallablePoint) { 
     // Your "constructor" code goes here. 
     // And don't return from here. 
    } else { 
     return new CallablePoint(x, y); 
    } 
} 

này sẽ trả về một thể hiện của CallablePoint bất kể nó như thế nào được gọi là:

var obj1 = CallablePoint(1,2); 
console.log(obj1 instanceof CallablePoint); // true 

var obj2 = new CallablePoint(1,2); 
console.log(obj2 instanceof CallablePoint); // true 
1

Tôi sẽ viết câu trả lời của tôi giả định rằng bạn đã sau __call__ chức năng có sẵn trong Python và thường được gọi là "đối tượng có thể gọi". "Đối tượng có thể gọi" có âm thanh lạ trong ngữ cảnh JavaScript.

Tôi đã thử một số công cụ JavaScript, nhưng không có công cụ nào trong số chúng tôi đã thử cho phép bạn gọi các đối tượng, ngay cả khi bạn kế thừa từ Function. Ví dụ:

function Callable(x) { 
...  "use strict"; 
...  this.__proto__ = Function.prototype; 
...  this.toString = function() { return x; }; 
... } 
undefined 
> var c = new Callable(42); 
var c = new Callable(42); 
undefined 
> c; 
c; 
{ toString: [function] } 
> c(42); 
c(42); 
TypeError: Property 'c' of object #<Object> is not a function 
    at repl:1:1 
    at REPLServer.eval (repl.js:80:21) 
    at repl.js:190:20 
    at REPLServer.eval (repl.js:87:5) 
    at Interface.<anonymous> (repl.js:182:12) 
    at Interface.emit (events.js:67:17) 
    at Interface._onLine (readline.js:162:10) 
    at Interface._line (readline.js:426:8) 
    at Interface._ttyWrite (readline.js:603:14) 
    at ReadStream.<anonymous> (readline.js:82:12) 
> c instanceof Function; 
c instanceof Function; 
true 
c.apply(null, [43]); 
TypeError: Function.prototype.apply was called on 43, which is a object and not a function 
    at Function.APPLY_PREPARE (native) 
    at repl:1:3 
    at REPLServer.eval (repl.js:80:21) 
    at repl.js:190:20 
    at REPLServer.eval (repl.js:87:5) 
    at Interface.<anonymous> (repl.js:182:12) 
    at Interface.emit (events.js:67:17) 
    at Interface._onLine (readline.js:162:10) 
    at Interface._line (readline.js:426:8) 
    at Interface._ttyWrite (readline.js:603:14) 
> 

Đây là V8 (Node.js). I E. bạn có thể có một đối tượng, chính thức thừa hưởng từ hàm, nhưng nó không thể gọi được, và tôi không thể tìm ra cách thuyết phục thời gian chạy nó có thể được gọi. Tôi đã có kết quả tương tự trong việc thực hiện JavaScrip của Mozilla, vì vậy tôi nghĩ rằng nó phải là phổ quát.

Tuy nhiên, vai trò của các loại tùy chỉnh trong JavaScript là biến mất nhỏ, vì vậy tôi không nghĩ rằng bạn sẽ bỏ lỡ nó nhiều. Nhưng, như bạn đã khám phá ra, bạn có thể tạo các thuộc tính trên các hàm, giống như cách bạn có thể làm với các đối tượng. Vì vậy, bạn có thể làm điều đó, chỉ trong một cách ít thuận tiện hơn.

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