2012-03-19 12 views
42

En Ruby, creo que puede llamar a un método que no se ha definido y, sin embargo, capturar el nombre del método llamado y hacer el procesamiento de este método en tiempo de ejecución.¿Tiene Javascript algo como la función method_missing de Ruby?

¿Puede Javascript hacer el mismo tipo de cosas?

+0

No estoy seguro de si se refiere a prototipos de funciones, en cuyo caso ¿puede ser útil? https: //developer.mozilla.org/es/JavaScript/Reference/Global_Objects/Function Para ser sincero, no tengo idea si esa referencia es útil. Solo he visto prototipos de funciones en C++ – Joel

+0

posible duplicado de [¿Falta el método de captura en Javascript y algo de lógica?] (http://stackoverflow.com/questions/8283362/capture-method-missing-in-javascript-and-do-some-logic) –

Respuesta

27

La característica de ruby ​​que está explicando se llama "method_missing" http://rubylearning.com/satishtalim/ruby_method_missing.htm.

Es una nueva característica que está presente solo en algunos navegadores como Firefox (en el motor Javascript de araña mono). En SpiderMonkey se llama "__noSuchMethod__" https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/NoSuchMethod

Lea este artículo de Yehuda Katz http://yehudakatz.com/2008/08/18/method_missing-in-javascript/ para obtener más detalles acerca de la próxima implementación.

+0

Gracias por los artículos, pensé que me desconcertaría mi pregunta extraña, ya que a menudo estoy aquí cuando hice preguntas de metaprogramación :) - Estoy feliz de que los gurús piensen al respecto. – user310291

+15

El artículo de Yehuda Katz es de 2008. Brandon Eich ha defendido Proxy API [desde 2010] (http://jsconf.eu/2010/speaker/be_proxy_objects.html). La API [\ _ \ _ noSuchMethod__] (https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/noSuchMethod) propuesta por Mozilla no es estándar y no tiene futuro. –

-1

Que yo sepa, pero puede simularlo inicializando la función a null al principio y luego reemplazando la implementación más tarde.

var foo = null; 
var bar = function() { alert(foo()); } // Appear to use foo before definition 

// ... 

foo = function() { return "ABC"; } /* Define the function */ 
bar(); /* Alert box pops up with "ABC" */ 

Este truco es similar a una C# truco para implementar lambdas recursivas, como se describe here.

El único inconveniente es que si haces uso foo antes de que sea definida, obtendrá un error de intentar llamar null como si se tratara de una función, en lugar de un mensaje de error más descriptivo. Pero esperaría obtener algún mensaje de error para usar una función antes de que esté definida.

+1

Todavía no lo quiero, ya que debe hacerse exclusivamente en tiempo de ejecución, mientras que en su ejemplo debe definir Foo en el momento del diseño, mientras que es posible que ni siquiera sepa el nombre foo en ese momento. – user310291

+0

"esperaría recibir algún mensaje de error para usar una función antes de que esté definida" - no, no lo haría. Ese es el punto de la falta de método. –

+0

No necesita inicializar foo para anular en la parte superior. La declaración se izará de todos modos. Siempre que se establezca antes de que se llame a la barra. De todos modos, esto realmente no responde la pregunta del OP ... – sstur

1

No, no hay una capacidad de metaprogramación en javascript directamente análoga a la de method_missing de ruby. El intérprete simplemente genera un error que el código de llamada puede detectar pero no puede ser detectado por el objeto al que se accede. Aquí hay algunas respuestas sobre la definición de funciones en tiempo de ejecución, pero eso no es lo mismo. Puedes hacer muchas metaprogramaciones, cambiar instancias específicas de objetos, definir funciones, hacer cosas funcionales como memorizar y decoradores. Pero no hay una metaprogramación dinámica de las funciones que faltan como las que hay en ruby ​​o python.

3

No en este momento, no. Hay a proposal for ECMAScript Harmony, called proxies, que implementa una función similar (en realidad, mucho más poderosa), pero ECMAScript Harmony aún no ha salido y probablemente no lo será en un par de años.

+1

Los proxies se implementan actualmente en Chrome 21 en adelante con el indicador experimental de Javascript. Consulte este sitio para obtener información actualizada sobre las funciones de ECMAScript Harmony actualmente admitidas: http://kangax.github.io/es5-compat-table/es6/ – stephenbez

+1

@ jörg-w-mittag, el futuro ya casi está aquí:) – IonicBurger

+1

@HappyHamburger: Estoy muy entusiasmado con ES6, específicamente Proper Tail Calls, 'let' y' const', sintaxis concisa de funciones y Proxies. –

41

method_missing no encaja bien con JavaScript por la misma razón que no existe en Python: en ambos lenguajes, los métodos son solo atributos que resultan ser funciones; y los objetos a menudo tienen atributos públicos que no se pueden llamar. Contraste con Ruby, donde la interfaz pública de un objeto es 100% de métodos.

Lo que se necesita en JavaScript es un gancho para obtener acceso a los atributos que faltan, ya sean métodos o no. Python lo tiene: vea el método especial __getattr__.

La propuesta __noSuchMethod__ de Mozilla introdujo otra inconsistencia en un lenguaje plagado de ellas.

El camino a seguir para JavaScript es la (también en ECMAscript Harmony), que está más cerca del protocolo Python para customizing attribute access que a method_missing de Ruby.

+3

Tenga en cuenta que la semántica de Javascript es un poco diferente y más complicada que en Python. En Python 'f = obj.m; f (x)' es equivalente a 'obj.m (x)'. En Javascript, 'obj.m (x)' establece 'this' en' obj', mientras que 'f = obj.m; f (x)' no. – ehabkost

+0

Amen: "La propuesta __noSuchMethod__ de Mozilla introdujo una nueva inconsistencia en un lenguaje plagado de ellos". – user2398029

1

Llegué a esta pregunta porque estaba buscando una manera de pasar a otro objeto si el método no estaba presente en el primer objeto. No es tan flexible como lo que preguntas; por ejemplo, si falta un método en ambos, se producirá un error.

Estaba pensando en hacer esto para una pequeña biblioteca que tengo que ayuda a configurar los objetos extjs de una manera que también los hace más comprobables. Tenía llamadas separadas para conseguir realmente el asimiento de los objetos de interacción y que esto podría ser una buena manera de pegarse esas llamadas juntas devolviendo efectivamente un tipo aumentada

me ocurren dos maneras de hacer esto:

Prototipos

Puede hacer esto usando prototipos, ya que las cosas caen en el prototipo si no están en el objeto real. Parece que esto no funcionaría si el conjunto de funciones que desea utilizar utiliza la palabra clave this: obviamente su objeto no sabrá o no le importará lo que el otro conoce.

Si todo es su propio código y no está usando esto y constructores ... que es una buena idea por muchas razones, entonces puede hacerlo de esta manera:

var makeHorse = function() { 
     var neigh = "neigh"; 

     return { 
      doTheNoise: function() { 
       return neigh + " is all im saying" 
      }, 
      setNeigh: function (newNoise) { 
       neigh = newNoise; 
      } 
     } 
    }; 

    var createSomething = function (fallThrough) { 
     var constructor = function() {}; 
     constructor.prototype = fallThrough; 
     var instance = new constructor(); 

     instance.someMethod = function() { 
      console.log("aaaaa"); 
     }; 
     instance.callTheOther = function() { 
      var theNoise = instance.doTheNoise(); 
      console.log(theNoise); 
     }; 

     return instance; 
    }; 

    var firstHorse = makeHorse(); 
    var secondHorse = makeHorse(); 
    secondHorse.setNeigh("mooo"); 

    var firstWrapper = createSomething(firstHorse); 
    var secondWrapper = createSomething(secondHorse); 
    var nothingWrapper = createSomething(); 

    firstWrapper.someMethod(); 
    firstWrapper.callTheOther(); 
    console.log(firstWrapper.doTheNoise()); 

    secondWrapper.someMethod(); 
    secondWrapper.callTheOther(); 
    console.log(secondWrapper.doTheNoise()); 

    nothingWrapper.someMethod(); 
    //this call fails as we dont have this method on the fall through object (which is undefined) 
    console.log(nothingWrapper.doTheNoise()); 

Esto no lo hace trabajo para mi caso de uso ya que los chicos de extjs no solo han usado erróneamente 'esto', sino que también han construido un sistema completo de tipo de herencia clásica loca sobre el principio del uso de prototipos y 'esto'.

Esta es realmente la primera vez que uso prototipos/constructores y estaba un poco desconcertado porque no puedes simplemente configurar el prototipo, también tienes que usar un constructor. Hay un campo mágico en los objetos (al menos en Firefox) llamado __proto que es básicamente el prototipo real. parece que el campo del prototipo real solo se usa en el momento de la construcción ... ¡qué confuso!


métodos de copia

Este método es probablemente más caro, pero parece más elegante para mí y también trabajará en el código que está utilizando this (por ejemplo, para que pueda usarlo para envolver objetos de la biblioteca). También funcionará en cosas escritas usando el estilo funcional/de cierre también. Lo acabo de ilustrar con this/constructors para mostrar que funciona con cosas como esa.

aquí está el mods:

//this is now a constructor 
    var MakeHorse = function() { 
     this.neigh = "neigh"; 
    }; 

    MakeHorse.prototype.doTheNoise = function() { 
     return this.neigh + " is all im saying" 
    }; 
    MakeHorse.prototype.setNeigh = function (newNoise) { 
     this.neigh = newNoise; 
    }; 

    var createSomething = function (fallThrough) { 
     var instance = { 
      someMethod : function() { 
       console.log("aaaaa"); 
      }, 
      callTheOther : function() { 
       //note this has had to change to directly call the fallThrough object 
       var theNoise = fallThrough.doTheNoise(); 
       console.log(theNoise); 
      } 
     }; 

     //copy stuff over but not if it already exists 
     for (var propertyName in fallThrough) 
      if (!instance.hasOwnProperty(propertyName)) 
       instance[propertyName] = fallThrough[propertyName]; 

     return instance; 
    }; 

    var firstHorse = new MakeHorse(); 
    var secondHorse = new MakeHorse(); 
    secondHorse.setNeigh("mooo"); 

    var firstWrapper = createSomething(firstHorse); 
    var secondWrapper = createSomething(secondHorse); 
    var nothingWrapper = createSomething(); 

    firstWrapper.someMethod(); 
    firstWrapper.callTheOther(); 
    console.log(firstWrapper.doTheNoise()); 

    secondWrapper.someMethod(); 
    secondWrapper.callTheOther(); 
    console.log(secondWrapper.doTheNoise()); 

    nothingWrapper.someMethod(); 
    //this call fails as we dont have this method on the fall through object (which is undefined) 
    console.log(nothingWrapper.doTheNoise()); 

realidad estaba anticipando tener que utilizar bind en alguna parte, pero no parece ser necesario.

1

He creado una biblioteca JavaScript que le permiten usar method_missing en javascript: https://github.com/ramadis/unmiss

Utiliza ES6 proxies para trabajar. Aquí hay un ejemplo que usa herencia de Clase ES6. Sin embargo, también puedes usar decoradores para lograr los mismos resultados.

import { MethodMissingClass } from 'unmiss' 

class Example extends MethodMissingClass { 
    methodMissing(name, ...args) { 
     console.log(`Method ${name} was called with arguments: ${args.join(' ')}`); 
    } 
} 

const instance = new Example; 
instance.what('is', 'this'); 

> Method what was called with arguments: is this 
Cuestiones relacionadas