Javascript ha qualcosa come la funzione method_missing di Ruby?

In Ruby penso che sia ansible chiamare un metodo che non è stato definito e tuttavia acquisire il nome del metodo chiamato e eseguire l’elaborazione di questo metodo in fase di runtime.

Javascript può fare lo stesso genere di cose?

La funzione ruby che stai spiegando si chiama “method_missing” http://rubylearning.com/satishtalim/ruby_method_missing.htm .

È una funzionalità nuova di zecca che è presente solo in alcuni browser come Firefox (nel motore Javascript Spider Monkey). In SpiderMonkey si chiama “__noSuchMethod__” https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/NoSuchMethod

Per favore leggi questo articolo di Yehuda Katz http://yehudakatz.com/2008/08/18/method_missing-in-javascript/ per maggiori dettagli sulla prossima implementazione.

method_missing non si adatta bene a JavaScript per lo stesso motivo per cui non esiste in Python: in entrambi i linguaggi, i metodi sono solo attributi che sono funzioni; e gli oggetti hanno spesso attributi pubblici che non sono richiamabili. Contrasto con Ruby, in cui l’interfaccia pubblica di un object è al 100% metodi.

Ciò che è necessario in JavaScript è un hook per catturare l’accesso agli attributi mancanti, che siano metodi o meno. Python lo ha: vedi il metodo speciale __getattr__ .

La proposta __noSuchMethod__ di Mozilla ha introdotto un’altra incongruenza in un linguaggio crivellato di loro.

La via da seguire per JavaScript è il meccanismo Proxy (anche in ECMAscript Harmony ), che è più vicino al protocollo Python per la personalizzazione dell’accesso agli attributi rispetto a method_missing di Ruby.

Non al momento, no. Esiste una proposta per ECMAScript Harmony, chiamata proxy , che implementa una funzione simile (in realtà molto più potente), ma ECMAScript Harmony non è ancora uscito e probabilmente non lo sarà per un paio d’anni.

Ho creato una libreria per javascript che ti permette di usare method_missing in javascript: https://github.com/ramadis/unmiss

Usa i proxy ES6 per funzionare. Ecco un esempio che utilizza l’ereditarietà della class ES6. Tuttavia, puoi anche usare i decoratori per ottenere gli stessi risultati.

 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 

No, non c’è capacità di metaprogrammazione in javascript direttamente analogo al hook method_missing di ruby. L’interprete solleva semplicemente un errore che il codice chiamante può catturare ma non può essere rilevato dall’object a cui si accede. Qui ci sono alcune risposte sulla definizione delle funzioni in fase di esecuzione, ma non è la stessa cosa. Puoi eseguire molte metaprogrammazione, cambiare istanze specifiche di oggetti, definire funzioni, fare cose funzionali come la memoizzazione e i decoratori. Ma non esiste una metaprogrammazione dynamic delle funzioni mancanti come in ruby o pitone.

Sono arrivato a questa domanda perché stavo cercando un modo per passare a un altro object se il metodo non era presente sul primo object. Non è così flessibile come quello che chiedi – ad esempio se manca un metodo da entrambi, allora fallirà.

Stavo pensando di fare questo per una piccola libreria che ho che aiuta a configurare gli oggetti extjs in un modo che li renda anche più testabili. Ho avuto chiamate separate per ottenere effettivamente gli oggetti per l’interazione e ho pensato che questo potrebbe essere un bel modo per attaccare quelle chiamate restituendo effettivamente un tipo aumentato

Posso pensare a due modi per farlo:

prototipi

Puoi farlo usando i prototipi, poiché le cose cadono nel prototipo se non si trovano sull’object reale. Sembra che questo non funzionerebbe se l’insieme delle funzioni che vuoi utilizzare passino a usare questa parola chiave – ovviamente il tuo object non saprà o non si preoccuperà delle cose che l’altro conosce.

Se è tutto il tuo codice e non stai usando questo e costruttori … che è una buona idea per molte ragioni, allora puoi farlo in questo modo:

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

Questo non funziona per il mio caso d’uso poiché i ragazzi di extjs non solo hanno erroneamente usato “questo” hanno anche costruito un intero sistema di tipo ereditario classico pazzesco sul principio dell’utilizzo di prototipi e di “questo”.

Questa è stata la prima volta che ho utilizzato prototipi / costruttori e sono rimasto un po ‘sconcertato sul fatto che non puoi semplicemente impostare il prototipo, devi anche usare un costruttore. C’è un campo magico negli oggetti (almeno in firefox) chiama __proto che è fondamentalmente il vero prototipo. sembra che il vero campo prototipo sia usato solo al momento della costruzione … che confusione!


Metodi di copia

Questo metodo è probabilmente più costoso, ma mi sembra più elegante e funzionerà anche con il codice che lo utilizza (ad esempio in modo da poterlo utilizzare per racchiudere gli oggetti della libreria). Funzionerà anche su cose scritte usando lo stile funzionale / di chiusura – l’ho appena illustrato con questo / costruttori per mostrare che funziona con cose del genere.

Ecco le mod:

  //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()); 

In realtà stavo anticipando di dover usare il bind da qualche parte ma sembra che non sia necessario.

Non a mia conoscenza, ma è ansible simularlo inizializzando inizialmente la funzione su null e quindi sostituendo l’implementazione in un secondo momento.

 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" */ 

Questo trucco è simile a un trucco C # per l’implementazione di lambda ricorsivi, come descritto qui .

L’unico svantaggio è che se usi foo prima che sia definito, riceverai un errore nel tentativo di chiamare null come se fosse una funzione, piuttosto che un messaggio di errore più descrittivo. Ma ci si aspetterebbe di ricevere un messaggio di errore per l’utilizzo di una funzione prima che sia definita.