var obj = new CallableObject(); obj(argumentos); Un objeto invocable es una estructura de datos que se comporta tanto como un objeto como una función. Puede acceder y asignar propiedades , métodos de llamada , sino también llamar al objeto directamente , como si fuera una función. obj.bar obj.foo() obj() La llamada directa es como llamar a un método de que tiene acceso a las propiedades del objeto a través de su obj this contexto Si tiene experiencia con Python, reconocerá esto, hay un protocolo de Python incorporado que usa el método de clase. Cualquier método asignado a se puede acceder como o como . __call__ __call__ obj.__call__() obj() Los objetos invocables también se pueden considerar como funciones con estado. Las funciones son procedimientos inherentemente de instancia única y sin estado. Los objetos invocables son procedimientos instanciados y con estado. En JavaScript casi todo es un objeto, incluidas las funciones, por lo que seguramente podemos hacer esto, pero ¿cómo? No está integrado en el lenguaje como Python, pero hay varias formas de hacerlo funcionar. El reto Inspirándonos en Python, queremos crear una Clase/constructor que podamos usar para crear objetos invocables que redirijan sus llamadas a un método llamado . Queremos esta redirección para que podamos heredar de nuestra Clase y anular y extender fácilmente el método con nueva funcionalidad, sin tener que preocuparse por el funcionamiento interno del objeto invocable. _call _call Para hacer esto, necesitaremos heredar del constructor de , que hereda de y nos permite crear tanto un objeto como una función dinámica. funciones Object Nuestro principal obstáculo es dar a un objeto de función una referencia a sí mismo. Para tener una referencia a la método, la parte de la función de nuestro objeto de función, generada por nuestra clase / constructor Callable, debe tener una referencia a sí misma. _call Las soluciones Queremos crear un extensible clase que mantiene una herencia adecuada y correcta en JavaScript, y nos permite llamar a los objetos que construye como funciones, con una referencia a ellos mismos, redirigiendo esas llamadas a un método anulable . Callable _call El camino de la vinculación { () { ( , ) ._bound = .bind( ) ._bound } _call(...args) { .log( , args) } } 'use strict' class Callable extends Function constructor super '...args' 'return this._bound._call(...args)' // Or without the spread/rest operator: // super('return this._bound._call.apply(this._bound, arguments)') this this this return this console this Pruébelo en repl.it Debido a que heredamos de , podemos crear funciones dinámicas a partir de cadenas, usando en nuestro constructor. Así que la cadena a la que le pasamos será el cuerpo de nuestra función. Queremos que esa función pueda acceder a su propio objeto y llamar a un método , transmitiendo sus argumentos. Hacemos esto usando . Function super super _call bind El método bind nos permitirá establecer el contexto de una función a lo que queramos, envolviendo esa función en una función de enlace transparente. Entonces unimos la función a sí misma con . this this.bind(this) Ahora nuestro objeto invocable tiene una referencia a sí mismo, excepto el objeto que devolvemos de nuestro constructor, devuelto por , es una versión envuelta de nuestro objeto original. Entonces, todas nuestras propiedades se adjuntarán a ese nuevo objeto de función envuelto, y nuestra función tiene una referencia al objeto antiguo pasado a . this.bind bind La solución fácil a esto es adjuntar una referencia al nuevo objeto envuelto en el objeto anterior como . Y el cuerpo de nuestra función, en la cadena pasada a , simplemente llama utilizando el referencia. _bound super _call this._bound ventajas No se basa en características obsoletas o modernas. No es necesario modificar prototipos. Contras Requiere envolver el objeto de función en una función enlazada. El camino de Callee { () { ( ) } _call(...args) { .log( , args) } } 'use strict' class Callable extends Function constructor super 'return arguments.callee._call.apply(arguments.callee, arguments)' // We can't use the rest operator because of the strict mode rules. // But we can use the spread operator instead of apply: // super('return arguments.callee._call(...arguments)') console this Pruébelo en repl.it Nuevamente usamos el llamada para crear una función dinámica, pero esta vez obtenemos nuestra referencia a la función en sí aprovechando otra variable implícita dentro de una función, el objeto de . super argumentos El objeto arguments tiene una propiedad que es una referencia a la función llamada. Usamos esta referencia como el primer argumento para que une las funciones contexto a sí mismo. arguments.callee apply this Entonces, dentro del cuerpo de la función, la cadena pasó a super, solo necesitamos llamar al método en . _call arguments.callee ventajas Muy simple. No es necesario modificar prototipos. Contras y no están disponibles en 'modo estricto', consulte para obtener más información. arguments arguments.callee MDN El código moderno debe evitar para el operador / . arguments spread rest La forma de cierre y prototipo { () { closure = { closure._call(...args) } .setPrototypeOf(closure, .target.prototype) } _call(...args) { .log( , args) } } 'use strict' class Callable extends Function constructor var ( ) function ...args return // Or without the spread/rest operator: // var closure = function() { // return closure._call.apply(closure, arguments) // } return Object new console this Pruébelo en repl.it Aquí en lugar de crear una función dinámica con , descartamos el objeto función creado por el constructor (el object ) y reemplácelo con un , devolviéndolo en lugar de desde el . super this closure this constructor El cierre también es un objeto de función, y puede referenciarse a sí mismo en su cuerpo a través del cerrado. variable. usamos el referencia para redirigir llamadas a su método . closure closure _call Pero hemos roto la cadena de prototipos reemplazando con , por lo que volvemos a adjuntar el prototipo del constructor a usando y (que es una referencia al constructor) para obtener el prototipo. this closure closure Object.setPrototypeOf new.target Puedes usar en vez de , pero primero debes llamar para crear el objeto, que es un despilfarro. this.constructor.prototype new.target.prototype super this ventajas No requiere envolver el objeto devuelto con un o . Proxy bind Contras Requiere modificar prototipos. La modificación de prototipos es lenta y tiene otros efectos secundarios, consulte . MDN El Camino Proxy { () { () ( , { : target._call(...args) }) } _call(...args) { .log( , args) } } 'use strict' class Callable extends Function constructor super return new Proxy this apply ( ) => target, thisArg, args console this Pruébelo en repl.it Usando podemos interceptar llamadas a una función, usando el atrapar y redirigirlo a otra función. los trampa le da a nuestro invocable una referencia a sí mismo como el argumento. Proxy apply apply target Entonces creamos una Clase que hereda de Función, , envolviendo los objetos invocables creados en un , interceptando cualquier llamada realizada a esos objetos y redirigiéndolas al método en el objeto mismo, usando el referencia. Callable Proxy _call target ventajas Forma sencilla y nativa de interceptar llamadas y redirigirlas. No es necesario modificar prototipos. Contras Requiere envolver objetos creados por en un . Callable Proxy Una pequeña penalización de rendimiento por usar manipuladores Proxy Lo que aprendimos JavaScript es un lenguaje extraordinariamente flexible, y puede doblarlo en muchas formas interesantes, como Objetos invocables. Es posible que haya casos de uso para los Objetos invocables, pero en general no recomendaría usarlos, no es JavaScript idiomático y puede ser confuso o poco claro para cualquier otra persona que use su código. Esto fue principalmente un ejercicio interesante. Referencias: https://stackoverflow.com/questions/36871299/how-to-extend-function-with-es6-classes/40878674#40878674