2012-09-29 9 views
10

Wie kann ich Konstruktor für Callable Object in JavaScript machen?Konstruktor für Callable Object in JavaScript

Ich habe verschiedene Möglichkeiten versucht, wie folgt. Das Beispiel dort ist nur gekürztes Beispiel des tatsächlichen Objekts.

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

Dies funktioniert auf den ersten, aber das Objekt ist es nicht Instanz von CallablePoint erzeugt, so dass es nicht kopieren Eigenschaften von CallablePoint.prototype und sagt false auf instanceof CallablePoint. Ist es möglich, Arbeitskonstruktor für Callable Object zu machen?

+2

Sie wissen, dass 'function point() {return point}' würde nur die Funktion selbst zurückgeben? Was hast du damit vor? Es würde Ihnen erlauben, 'p()()()()()()()()()' aber was ist der Punkt? (kein Wortspiel beabsichtigt). –

+2

@FelixKling Ich musste mich verständlicherweise von der Beantwortung dieser Frage verabschieden – Pointy

+0

Der Vollständigkeit halber hat ES6 eine bessere Lösung dafür, siehe: http://stackoverflow.com/questions/36871299/how-to-e-tend-function-with -es6-classes – 0xc0de

Antwort

19

Es ist tatsächlich möglich. Wenn die Funktion entweder mit function Syntax oder Function Konstruktor erstellt wird, erhält es interne [[Call]] Eigenschaft. Es ist keine Eigenschaft der Funktion selbst, sondern eine Eigenschaft, die jede Funktion erhält, wenn sie konstruiert wird.

Während das nur bedeutet, dass irgendetwas mit [[Call]] könnte nur Function wenn es gebaut ist (na ja, mit einer Ausnahme nicht – Function.prototype selbst, die nicht von Function erbt), dass es etwas anderes bedeutet nicht, nicht später werden kann, unter Beibehaltung [[Call]] Eigentum. Nun, vorausgesetzt, Ihr Browser ist kein IE

Die Sache, die erlaubt, die Magie zu ändern wäre __proto__ von ES6, bereits in vielen Browsern implementiert. __proto__ ist eine magische Eigenschaft, die aktuellen Prototyp enthält. Indem ich es ändere, kann ich eine Funktion erstellen, die von etwas erbt, das nicht Function ist.

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) 

Zuerst wird der Konstruktor für CallablePoint macht ein Function (nur Function s sind erlaubt mit [[Call]] Eigentum beginnen. Als nächstes ich ihr Vorbild zu ändern, so wäre es CallablePoint zu erben. An dieser Stelle habe ich eine Funktion, die doesn ‚t von Function (Art von verwirrenden) erben.

Nachdem I definiert Konstruktor für CallablePoint s, habe ich den Prototyp zu CallablePointFunction, so habe ich CallablePoint, die von Function erbt.

Auf diese Weise haben die CallablePoint Instanzen Prototyp-Kette: CallablePoint -> Function -> Object, während noch aufrufbar sein. Da das Objekt auch aufrufbar ist, hat es gemäß der Spezifikation typeof gleich 'function'.

-1

Wenn Sie den CallablePoint() Konstruktor wollen ein Objekt vom Typ CallablePoint zurück, dann können Sie etwas tun, wo das CallablePoint Objekt einen Punkt als Eigenschaft des Objekts enthält, bleibt aber ein CallablePoint Objekt:

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

oder, wenn das, was Sie versuchen, wirklich zu tun ist, um eine Funktion zu machen, dass Sie ein Objekt von CallablePoint zurückgibt, dann können Sie einen Faktor Funktion erstellen:

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

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

I * denke * der Wunsch ist, dass der Rückgabewert eine Funktion ist. Ich kann jedoch nicht herausfinden warum. – Pointy

+0

@ Pointy - Ich stimme zu, es ist nicht klar, was genau das OP versucht zu tun. Ich habe eine andere Option hinzugefügt. – jfriend00

+2

Ihr 'Punkt.x' ist nicht der, den Sie gerade in' this.point' erstellt haben. – Alnitak

0

ich bin, wenn Sie nicht sicher sind, Beachten Sie, dass Ihr Objekt nur eine Instanz ist von CallablePoint, wenn Sie das Schlüsselwort new verwenden. Indem Sie es "aufrufbar" nennen, lassen Sie mich glauben, dass Sie nicht verwenden möchten, um new zu verwenden. Auf jeden Fall gibt es eine Möglichkeit, eine Instanz zu zwingen, zurückgeschickt wird (Dank für die Spitze, 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); 
    } 
} 

Dies wird eine Instanz von CallablePoint unabhängig davon zurückgeben, wie es hieß:

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

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

Ich werde meine Antwort schreiben unter der Annahme, dass Sie nach __call__ Funktionalität in Python verfügbar waren und oft als "Callable Object" bezeichnet. "Callable object" klingt im JavaScript-Kontext fremdartig.

Ich habe mehrere JavaScript-Engines ausprobiert, aber keines, das ich ausprobiert habe, erlaubt es Ihnen, Objekte aufzurufen, selbst wenn Sie von Function erben. Zum Beispiel:

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) 
> 

Dies ist V8 (Node.js). I.e. Sie haben vielleicht ein Objekt, das formal von der Funktion erbt, aber es ist nicht aufrufbar, und ich konnte keinen Weg finden, die Laufzeit, die es aufgerufen werden könnte, zu überzeugen. Ich hatte ähnliche Ergebnisse in Mozilla's Implementierung von JavaScrip, also denke ich, dass es universell sein muss.

Allerdings ist die Rolle von benutzerdefinierten Typen in JavaScript verschwindend klein, also glaube ich nicht, dass Sie es so sehr vermissen werden. Wie Sie jedoch bereits festgestellt haben, können Sie Eigenschaften für Funktionen genauso wie für Objekte erstellen. Also, Sie können es tun, nur auf eine weniger bequeme Art und Weise.