2012-09-29 81 views
10

¿Cómo puedo crear un constructor para un objeto llamable en JavaScript?Constructor para objeto invocable en JavaScript

He intentado varias formas, como seguir. El ejemplo es un ejemplo abreviado de objeto real.

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

Esto funciona en un primer momento, pero el objeto que crea no es instancia de CallablePoint, por lo que no copia las propiedades de CallablePoint.prototype y dice false en instanceof CallablePoint. ¿Es posible hacer un constructor que funcione para un objeto llamable?

+2

Usted sabe que 'punto de función() {}' punto de retorno sólo devuelve la función en sí? ¿Qué piensas con esto? Te permitiría hacer 'p()()()()()()()()()' pero ¿cuál es el punto? (sin juego de palabras) –

+2

@FelixKling comprensiblemente tuve que recusarme de contestar esta pregunta – Pointy

+0

Para mayor información, ES6 tiene una mejor solución para esto, vea: http://stackoverflow.com/questions/36871299/how-to-extend-function-with -es6-classes – 0xc0de

Respuesta

19

Resulta que en realidad es posible. Cuando se crea la función, ya sea utilizando la sintaxis function o el constructor Function, obtiene la propiedad interna [[Call]]. No es una propiedad de la función en sí misma, sino propiedad que cualquier función obtiene cuando se construye.

Mientras que sólo significa que cualquier cosa con [[Call]] podría ser solamente Function cuando se construyó (bueno, hay una excepción – Function.prototype sí que no hereda de Function), eso no quiere decir que no puede convertirse en algo más tarde, preservando la propiedad [[Call]]. Bueno, siempre que su navegador no sea IE < 11.

Lo que permite cambiar la magia sería __proto__ de ES6, ya implementado en muchos navegadores. __proto__ es una propiedad mágica que contiene el prototipo actual. Al cambiarlo, puedo hacer que la función herede de algo que no es 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) 

En primer lugar, el constructor de CallablePoint hace un Function (sólo Function s se les permite comenzar con [[Call]] propiedad. A continuación, cambie su prototipo, por lo que heredarían CallablePoint. En este punto, tengo una función que doesn 't heredan de Function (especie de confuso).

Después he definido constructor para CallablePoint s, he de poner el prototipo de CallablePoint a Function, así que tengo CallablePoint que hereda de Function.

De esta forma, las instancias CallablePoint tienen una cadena de prototipo: CallablePoint -> Function -> Object, aunque siguen siendo invocables. Además, como el objeto es invocable, tiene según la especificación typeof igual a 'function'.

-1

Si desea que el constructor CallablePoint() para devolver un objeto de tipo CallablePoint, a continuación, puede hacer algo como esto, donde el objeto CallablePoint contiene un punto como una propiedad del objeto, pero sigue siendo un objeto CallablePoint:

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

o, si lo que realmente está tratando de hacer es hacer una función que devuelve un objeto de CallablePoint, a continuación, puede crear una función de factor:

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 * think * el deseo es que el valor de retorno sea una función. No puedo entender por qué, sin embargo. – Pointy

+0

@Pointy - Estoy de acuerdo en que no está claro exactamente qué está tratando de hacer el OP. He agregado otra opción. – jfriend00

+2

Su 'point.x' no es el que acaba de crear en' this.point' – Alnitak

0

no estoy seguro de si estás consciente de que su objeto solo será una instancia de CallablePoint si usa la palabra clave new. Al denominarlo "invocable", me haces pensar que no desea querer para usar new. De todos modos, hay una manera de forzar una instancia para ser devuelto (gracias por la punta, 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); 
    } 
} 

Esto devolverá una instancia de CallablePoint independientemente de cómo se llamaba:

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

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

Escribiré mi respuesta asumiendo que estaba después de la funcionalidad __call__ disponible en Python y a menudo denominado "objeto invocable". El "objeto invocable" suena extraño en el contexto de JavaScript.

He intentado varios motores de JavaScript, pero ninguno de los que he probado le permite llamar a objetos, incluso si hereda de Function. Por ejemplo:

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

Esto es V8 (Node.js). Es decir. puede tener un objeto, que hereda formalmente de la función, pero no se puede llamar, y no pude encontrar una manera de convencer al tiempo de ejecución de que se llame. Obtuve resultados similares en la implementación de JavaScrip de Mozilla, así que creo que debe ser universal.

Sin embargo, el rol de los tipos personalizados en JavaScript es muy pequeño, así que no creo que de todos modos te lo pierda. Pero, como ya había descubierto, puede crear propiedades en las funciones, de la misma manera que puede hacerlo en los objetos. Entonces, puedes hacerlo, solo de una manera menos conveniente.

Cuestiones relacionadas