Come estendere la funzione con le classi ES6?


105

ES6 permette di estendere oggetti speciali. Quindi è possibile ereditare dalla funzione. Tale oggetto può essere chiamato come funzione, ma come posso implementare la logica per tale chiamata?

class Smth extends Function {
  constructor (x) {
    // What should be done here
    super();
  }
}

(new Smth(256))() // to get 256 at this call?

Qualsiasi metodo di classe ottiene riferimento all'istanza di classe tramite this. Ma quando viene chiamato come funzione, si thisriferisce a window. Come posso ottenere il riferimento all'istanza della classe quando viene chiamata come funzione?

PS: stessa domanda in russo.


17
Ah, finalmente qualcuno ha chiesto a questa queston :-)
Bergi

1
Basta fare super(x)(cioè passare a Function)? Non sono sicuro che Functionpossa effettivamente essere esteso però.
Felix Kling

Tieni presente che ci sono ancora problemi con l'estensione delle classi incorporate. Le specifiche suggeriscono che dovrebbe essere possibile, ma ho riscontrato problemi di estensione Error, tra gli altri.
ssube

1
Tieni presente che Functionè semplicemente un costruttore di funzioni. L'implementazione della funzione deve essere passata al costruttore. Se non vuoi Smthaccettare un'implementazione, devi fornirla nel costruttore, ad es super('function implementation here').
Felix Kling

1
@ Qwertiy: direi che questa è l' eccezione , non il caso generale. Anche questo è molto specifico per le espressioni di funzione , ma stai usando il Functioncostruttore (runtime) che è molto diverso da un'espressione di funzione (sintassi).
Felix Kling

Risposte:


49

La superchiamata richiamerà il Functioncostruttore, che si aspetta una stringa di codice. Se desideri accedere ai dati della tua istanza, puoi semplicemente codificarli:

class Smth extends Function {
  constructor(x) {
    super("return "+JSON.stringify(x)+";");
  }
}

ma non è davvero soddisfacente. Vogliamo usare una chiusura.

Avere la funzione restituita come una chiusura che può accedere alle variabili di istanza è possibile, ma non facile. La cosa buona è che non devi chiamare superse non vuoi - puoi ancora returnoggetti arbitrari dai tuoi costruttori di classi ES6. In questo caso, lo faremmo

class Smth extends Function {
  constructor(x) {
    // refer to `smth` instead of `this`
    function smth() { return x; };
    Object.setPrototypeOf(smth, Smth.prototype);
    return smth;
  }
}

Ma possiamo fare anche di meglio e astrarre questa cosa da Smth:

class ExtensibleFunction extends Function {
  constructor(f) {
    return Object.setPrototypeOf(f, new.target.prototype);
  }
}

class Smth extends ExtensibleFunction {
  constructor(x) {
    super(function() { return x; }); // closure
    // console.log(this); // function() { return x; }
    // console.log(this.prototype); // {constructor: …}
  }
}
class Anth extends ExtensibleFunction {
  constructor(x) {
    super(() => { return this.x; }); // arrow function, no prototype object created
    this.x = x;
  }
}
class Evth extends ExtensibleFunction {
  constructor(x) {
    super(function f() { return f.x; }); // named function
    this.x = x;
  }
}

Certo, questo crea un ulteriore livello di riferimento indiretto nella catena di ereditarietà, ma non è necessariamente una cosa negativa (puoi estenderlo invece del nativo Function). Se vuoi evitarlo, usa

function ExtensibleFunction(f) {
  return Object.setPrototypeOf(f, new.target.prototype);
}
ExtensibleFunction.prototype = Function.prototype;

ma si noti che Smthnon erediterà dinamicamente le Functionproprietà statiche .


Voglio ottenere l'accesso allo stato della classe dalla funzione.
Qwertiy

2
@ Qwertiy: Allora usa il secondo suggerimento di Bergi.
Felix Kling

@ AlexanderO'Mara: Non puoi cambiare il prototipo della funzione se vuoi che le tue Smthistanze siano instanceof Smth(come tutti si aspetterebbero). Puoi omettere la Object.setPrototypeOfchiamata se non hai bisogno di questo o di uno qualsiasi dei tuoi metodi prototipo dichiarati nella tua classe.
Bergi

@ AlexanderO'Mara: Inoltre Object.setPrototypeOfnon è un grande rischio di ottimizzazione fintanto che viene fatto subito dopo aver creato l'oggetto. È solo se muti il ​​[[prototipo]] di un oggetto avanti e indietro durante la sua vita che sarà cattivo.
Bergi

1
@amn No, non lo fai, quando non usi thise returnun oggetto.
Bergi

32

Si tratta di un approccio alla creazione di oggetti richiamabili che fanno correttamente riferimento ai membri dell'oggetto e mantengono l'ereditarietà corretta, senza interferire con i prototipi.

Semplicemente:

class ExFunc extends Function {
  constructor() {
    super('...args', 'return this.__self__.__call__(...args)')
    var self = this.bind(this)
    this.__self__ = self
    return self
  }

  // Example `__call__` method.
  __call__(a, b, c) {
    return [a, b, c];
  }
}

Estendi questa classe e aggiungi un __call__metodo, più sotto ...

Una spiegazione in codice e commenti:

// This is an approach to creating callable objects
// that correctly reference their own object and object members,
// without messing with prototypes.

// A Class that extends Function so we can create
// objects that also behave like functions, i.e. callable objects.
class ExFunc extends Function {
  constructor() {
    super('...args', 'return this.__self__.__call__(...args)');
    // Here we create a function dynamically using `super`, which calls
    // the `Function` constructor which we are inheriting from. Our aim is to create
    // a `Function` object that, when called, will pass the call along to an internal
    // method `__call__`, to appear as though the object is callable. Our problem is
    // that the code inside our function can't find the `__call__` method, because it
    // has no reference to itself, the `this` object we just created.
    // The `this` reference inside a function is called its context. We need to give
    // our new `Function` object a `this` context of itself, so that it can access
    // the `__call__` method and any other properties/methods attached to it.
    // We can do this with `bind`:
    var self = this.bind(this);
    // We've wrapped our function object `this` in a bound function object, that
    // provides a fixed context to the function, in this case itself.
    this.__self__ = self;
    // Now we have a new wrinkle, our function has a context of our `this` object but
    // we are going to return the bound function from our constructor instead of the
    // original `this`, so that it is callable. But the bound function is a wrapper
    // around our original `this`, so anything we add to it won't be seen by the
    // code running inside our function. An easy fix is to add a reference to the
    // new `this` stored in `self` to the old `this` as `__self__`. Now our functions
    // context can find the bound version of itself by following `this.__self__`.
    self.person = 'Hank'
    return self;
  }
  
  // An example property to demonstrate member access.
  get venture() {
    return this.person;
  }
  
  // Override this method in subclasses of ExFunc to take whatever arguments
  // you want and perform whatever logic you like. It will be called whenever
  // you use the obj as a function.
  __call__(a, b, c) {
    return [this.venture, a, b, c];
  }
}

// A subclass of ExFunc with an overridden __call__ method.
class DaFunc extends ExFunc {
  constructor() {
    super()
    this.a = 'a1'
    this.b = 'b2'
    this.person = 'Dean'
  }

  ab() {
    return this.a + this.b
  }
  
  __call__(ans) {
    return [this.ab(), this.venture, ans];
  }
}

// Create objects from ExFunc and its subclass.
var callable1 = new ExFunc();
var callable2 = new DaFunc();

// Inheritance is correctly maintained.
console.log('\nInheritance maintained:');
console.log(callable2 instanceof Function);  // true
console.log(callable2 instanceof ExFunc);  // true
console.log(callable2 instanceof DaFunc);  // true

// Test ExFunc and its subclass objects by calling them like functions.
console.log('\nCallable objects:');
console.log( callable1(1, 2, 3) );  // [ 'Hank', 1, 2, 3 ]
console.log( callable2(42) );  // [ 'a1b2', Dean', 42 ]

// Test property and method access
console.log(callable2.a, callable2.b, callable2.ab())

Visualizza su repl.it

Ulteriore spiegazione di bind:

function.bind()funziona in modo molto simile function.call()e condividono una firma del metodo simile:

fn.call(this, arg1, arg2, arg3, ...);di più su mdn

fn.bind(this, arg1, arg2, arg3, ...);di più su mdn

In entrambi il primo argomento ridefinisce il thiscontesto all'interno della funzione. È inoltre possibile associare argomenti aggiuntivi a un valore. Ma dove callchiama immediatamente la funzione con i valori associati, bindrestituisce un oggetto funzione "esotico" che avvolge in modo trasparente l'originale, con thise qualsiasi argomento preimpostato.

Quindi, quando definisci una funzione, bindalcuni dei suoi argomenti:

var foo = function(a, b) {
  console.log(this);
  return a * b;
}

foo = foo.bind(['hello'], 2);

Chiami la funzione legata solo con gli argomenti rimanenti, il suo contesto è preimpostato, in questo caso a ['hello'].

// We pass in arg `b` only because arg `a` is already set.
foo(2);  // returns 4, logs `['hello']`

Puoi aggiungere una spiegazione del perché bindfunziona (cioè perché restituisce un'istanza di ExFunc)?
Bergi

@Bergi bindrestituisce un oggetto funzione trasparente che avvolge l'oggetto funzione su cui è stato chiamato, che è il nostro oggetto richiamabile, solo con il thisrimbalzo del contesto. Quindi restituisce davvero un'istanza racchiusa in modo trasparente di ExFunc. Post aggiornato con maggiori informazioni su bind.
Adrien

1
@Bergi Tutti i getter / setter ei metodi sono accessibili, le proprietà / attributi devono essere assegnati nel constructordopo bindin ExFunc. Nelle sottoclassi di ExFunc, tutti i membri sono accessibili. Quanto a instanceof; in es6 le funzioni associate sono indicate come esotiche, quindi il loro funzionamento interno non è evidente, ma penso che passi la chiamata al suo obiettivo avvolto, tramite Symbol.hasInstance. È molto simile a un proxy, ma è un metodo semplice per ottenere l'effetto desiderato. La loro firma è simile non la stessa.
Adrien

1
@Adrien ma dall'interno __call__non riesco ad accedere this.ao this.ab(). ad esempio repl.it/repls/FelineFinishedDesktopenvironment
Rob

1
@rob ben individuato, c'è un errore di riferimento, ho aggiornato la risposta e il codice con una correzione e una nuova spiegazione.
Adrien

20

Puoi avvolgere l'istanza Smth in un proxy con una apply(e forse construct) trappola:

class Smth extends Function {
  constructor (x) {
    super();
    return new Proxy(this, {
      apply: function(target, thisArg, argumentsList) {
        return x;
      }
    });
  }
}
new Smth(256)(); // 256

Bella idea. Come questo. Devo implementare più logica invece di inserirla all'interno dell'applicazione?
Qwertiy

4
Un proxy dovrebbe sostenere un bel po 'di overhead, non è vero? Inoltre, thisè ancora una funzione vuota (controllo new Smth().toString()).
Bergi

2
@Bergi Nessuna idea sulle prestazioni. MDN ha un grande avvertimento rosso in grassetto setPrototypeOfe non dice nulla sui proxy. Ma immagino che i proxy possano essere problematici quanto setPrototypeOf. E a proposito toString, può essere ombreggiato con un metodo personalizzato in Smth.prototype. Quello nativo è comunque dipendente dall'implementazione.
Oriol

@Qwertiy È possibile aggiungere una constructtrap per specificare il comportamento di new new Smth(256)(). E aggiungi metodi personalizzati che ombreggiano quelli nativi che accedono al codice di una funzione, come ha toStringnotato Bergi.
Oriol

Il tuo applymetodo è implementato nel modo in cui dovrebbe essere utilizzato, o è solo una dimostrazione e ho bisogno di esaminare più informazioni Proxye Reflectdi usarlo in modo corretto?
Qwertiy

3

Ho seguito il consiglio della risposta di Bergi e l'ho inserito in un modulo NPM .

var CallableInstance = require('callable-instance');

class ExampleClass extends CallableInstance {
  constructor() {
    // CallableInstance accepts the name of the property to use as the callable
    // method.
    super('instanceMethod');
  }

  instanceMethod() {
    console.log("instanceMethod called!");
  }
}

var test = new ExampleClass();
// Invoke the method normally
test.instanceMethod();
// Call the instance itself, redirects to instanceMethod
test();
// The instance is actually a closure bound to itself and can be used like a
// normal function.
test.apply(null, [ 1, 2, 3 ]);

3

Aggiornare:

Sfortunatamente questo non funziona perché ora restituisce un oggetto funzione invece di una classe, quindi sembra che in realtà non possa essere fatto senza modificare il prototipo. Noioso.


Fondamentalmente il problema è che non c'è modo di impostare il thisvalore per il Functioncostruttore. L'unico modo per farlo davvero sarebbe usare il.bind metodo in seguito, tuttavia non è molto adatto alla classe.

Potremmo farlo in una classe base helper, tuttavia thisnon diventa disponibile fino a dopo la superchiamata iniziale , quindi è un po 'complicato.

Esempio di lavoro:

'use strict';

class ClassFunction extends function() {
    const func = Function.apply(null, arguments);
    let bound;
    return function() {
        if (!bound) {
            bound = arguments[0];
            return;
        }
        return func.apply(bound, arguments);
    }
} {
    constructor(...args) {
        (super(...args))(this);
    }
}

class Smth extends ClassFunction {
    constructor(x) {
        super('return this.x');
        this.x = x;
    }
}

console.log((new Smth(90))());

(L'esempio richiede un browser moderno o node --harmony.)

Fondamentalmente la funzione di base si ClassFunctionestende avvolgerà la Functionchiamata del costruttore con una funzione personalizzata che è simile a .bind, ma consente l'associazione in un secondo momento, alla prima chiamata. Quindi nel ClassFunctioncostruttore stesso, chiama la funzione restituita da supercui è ora la funzione associata, passando thisper completare l'impostazione della funzione di associazione personalizzata.

(super(...))(this);

Tutto questo è un po 'complicato, ma evita di modificare il prototipo, che è considerato di cattivo gusto per motivi di ottimizzazione e può generare avvisi nelle console dei browser.


1
Stai complicando le cose. boundfarà riferimento alla funzione che hai returnda quella classe anonima. Basta nominarlo e fare riferimento direttamente ad esso. Consiglierei anche di evitare di passare stringhe di codice, sono solo un casino con cui lavorare (in ogni fase del processo di sviluppo).
Bergi

Questo extendsnon sembra funzionare come previsto, poiché Function.isPrototypeOf(Smth)e new Smth instanceof Functionsono anche false.
Bergi

@Bergi Quale motore JS stai usando? console.log((new Smth) instanceof Function);è trueper me in Node v5.11.0 e l'ultimo Firefox.
Alexander O'Mara

Ops, esempio sbagliato. È new Smth instanceof Smthche non funziona con la tua soluzione. Inoltre, nessun metodo Smthsarà disponibile sulle tue istanze, poiché restituisci solo uno standard Function, non un file Smth.
Bergi

1
@Bergi Darn it, sembra che tu abbia ragione. Tuttavia l'estensione di qualsiasi tipo nativo sembra avere lo stesso problema. extend Functionrende anche new Smth instanceof Smthfalso.
Alexander O'Mara

1

In primo luogo sono arrivato alla soluzione arguments.callee, ma è stato orribile.
Mi aspettavo che si rompesse in modalità rigorosa globale, ma sembra che funzioni anche lì.

class Smth extends Function {
  constructor (x) {
    super('return arguments.callee.x');
    this.x = x;
  }
}

(new Smth(90))()

Era un brutto modo a causa dell'uso arguments.callee, del passaggio del codice come stringa e della forzatura della sua esecuzione in modalità non rigorosa. Ma è applyapparsa l' idea di ignorare .

var global = (1,eval)("this");

class Smth extends Function {
  constructor(x) {
    super('return arguments.callee.apply(this, arguments)');
    this.x = x;
  }
  apply(me, [y]) {
    me = me !== global && me || this;
    return me.x + y;
  }
}

E il test, che mostra che sono in grado di eseguirlo come funzione in diversi modi:

var f = new Smth(100);

[
f instanceof Smth,
f(1),
f.call(f, 2),
f.apply(f, [3]),
f.call(null, 4),
f.apply(null, [5]),
Function.prototype.apply.call(f, f, [6]),
Function.prototype.apply.call(f, null, [7]),
f.bind(f)(8),
f.bind(null)(9),
(new Smth(200)).call(new Smth(300), 1),
(new Smth(200)).apply(new Smth(300), [2]),
isNaN(f.apply(window, [1])) === isNaN(f.call(window, 1)),
isNaN(f.apply(window, [1])) === isNaN(Function.prototype.apply.call(f, window, [1])),
] == "true,101,102,103,104,105,106,107,108,109,301,302,true,true"

Versione con

super('return arguments.callee.apply(arguments.callee, arguments)');

infatti contiene bindfunzionalità:

(new Smth(200)).call(new Smth(300), 1) === 201

Versione con

super('return arguments.callee.apply(this===(1,eval)("this") ? null : this, arguments)');
...
me = me || this;

rende calle applysu windowincoerenti:

isNaN(f.apply(window, [1])) === isNaN(f.call(window, 1)),
isNaN(f.apply(window, [1])) === isNaN(Function.prototype.apply.call(f, window, [1])),

quindi l'assegno dovrebbe essere spostato in apply:

super('return arguments.callee.apply(this, arguments)');
...
me = me !== global && me || this;

1
Cosa stai effettivamente cercando di fare?
Grazie

2
Penso che le classi siano sempre in modalità rigorosa: stackoverflow.com/questions/29283935/…
Alexander O'Mara

@ AlexanderO'Mara, a proposito, thisè una finestra, non undefined, quindi la funzione creata non è in modalità strict (almeno in chrome).
Qwertiy

Per favore, smettila di sminuire questa risposta. Ho già scritto che è un brutto modo. Ma è davvero una risposta: funziona sia in FF che in Chrome (non devi controllare Edge).
Qwertiy

Immagino che funzioni perché Functionnon è in modalità rigorosa. Sebbene orribile, è interessante +1. Probabilmente però non saresti più in grado di camminare su una catena.
Alexander O'Mara

1

Questa è la soluzione che ho elaborato che soddisfa tutte le mie esigenze di estensione delle funzioni e mi ha soddisfatto abbastanza bene. I vantaggi di questa tecnica sono:

  • Quando si estende ExtensibleFunction, il codice è idiomatico di estendere qualsiasi classe ES6 (no, scherzare con finti costruttori o proxy).
  • La catena del prototipo viene mantenuta in tutte le sottoclassi e instanceof/ .constructorrestituisce i valori attesi.
  • .bind() .apply()e .call()tutto funziona come previsto. Questo viene fatto sovrascrivendo questi metodi per alterare il contesto della funzione "interna" in contrasto con l' ExtensibleFunctionistanza (o la sua sottoclasse).
  • .bind()restituisce una nuova istanza del costruttore di funzioni (sia esso ExtensibleFunctiono una sottoclasse). Viene utilizzato Object.assign()per garantire che le proprietà memorizzate nella funzione associata siano coerenti con quelle della funzione di origine.
  • Le chiusure vengono rispettate e le funzioni delle frecce continuano a mantenere il contesto corretto.
  • La funzione "interna" è memorizzata tramite un Symbol, che può essere offuscato da moduli o un IIFE (o qualsiasi altra tecnica comune di privatizzazione dei riferimenti).

E senza ulteriori indugi, il codice:

// The Symbol that becomes the key to the "inner" function 
const EFN_KEY = Symbol('ExtensibleFunctionKey');

// Here it is, the `ExtensibleFunction`!!!
class ExtensibleFunction extends Function {
  // Just pass in your function. 
  constructor (fn) {
    // This essentially calls Function() making this function look like:
    // `function (EFN_KEY, ...args) { return this[EFN_KEY](...args); }`
    // `EFN_KEY` is passed in because this function will escape the closure
    super('EFN_KEY, ...args','return this[EFN_KEY](...args)');
    // Create a new function from `this` that binds to `this` as the context
    // and `EFN_KEY` as the first argument.
    let ret = Function.prototype.bind.apply(this, [this, EFN_KEY]);
    // For both the original and bound funcitons, we need to set the `[EFN_KEY]`
    // property to the "inner" function. This is done with a getter to avoid
    // potential overwrites/enumeration
    Object.defineProperty(this, EFN_KEY, {get: ()=>fn});
    Object.defineProperty(ret, EFN_KEY, {get: ()=>fn});
    // Return the bound function
    return ret;
  }

  // We'll make `bind()` work just like it does normally
  bind (...args) {
    // We don't want to bind `this` because `this` doesn't have the execution context
    // It's the "inner" function that has the execution context.
    let fn = this[EFN_KEY].bind(...args);
    // Now we want to return a new instance of `this.constructor` with the newly bound
    // "inner" function. We also use `Object.assign` so the instance properties of `this`
    // are copied to the bound function.
    return Object.assign(new this.constructor(fn), this);
  }

  // Pretty much the same as `bind()`
  apply (...args) {
    // Self explanatory
    return this[EFN_KEY].apply(...args);
  }

  // Definitely the same as `apply()`
  call (...args) {
    return this[EFN_KEY].call(...args);
  }
}

/**
 * Below is just a bunch of code that tests many scenarios.
 * If you run this snippet and check your console (provided all ES6 features
 * and console.table are available in your browser [Chrome, Firefox?, Edge?])
 * you should get a fancy printout of the test results.
 */

// Just a couple constants so I don't have to type my strings out twice (or thrice).
const CONSTRUCTED_PROPERTY_VALUE = `Hi, I'm a property set during construction`;
const ADDITIONAL_PROPERTY_VALUE = `Hi, I'm a property added after construction`;

// Lets extend our `ExtensibleFunction` into an `ExtendedFunction`
class ExtendedFunction extends ExtensibleFunction {
  constructor (fn, ...args) {
    // Just use `super()` like any other class
    // You don't need to pass ...args here, but if you used them
    // in the super class, you might want to.
    super(fn, ...args);
    // Just use `this` like any other class. No more messing with fake return values!
    let [constructedPropertyValue, ...rest] = args;
    this.constructedProperty = constructedPropertyValue;
  }
}

// An instance of the extended function that can test both context and arguments
// It would work with arrow functions as well, but that would make testing `this` impossible.
// We pass in CONSTRUCTED_PROPERTY_VALUE just to prove that arguments can be passed
// into the constructor and used as normal
let fn = new ExtendedFunction(function (x) {
  // Add `this.y` to `x`
  // If either value isn't a number, coax it to one, else it's `0`
  return (this.y>>0) + (x>>0)
}, CONSTRUCTED_PROPERTY_VALUE);

// Add an additional property outside of the constructor
// to see if it works as expected
fn.additionalProperty = ADDITIONAL_PROPERTY_VALUE;

// Queue up my tests in a handy array of functions
// All of these should return true if it works
let tests = [
  ()=> fn instanceof Function, // true
  ()=> fn instanceof ExtensibleFunction, // true
  ()=> fn instanceof ExtendedFunction, // true
  ()=> fn.bind() instanceof Function, // true
  ()=> fn.bind() instanceof ExtensibleFunction, // true
  ()=> fn.bind() instanceof ExtendedFunction, // true
  ()=> fn.constructedProperty == CONSTRUCTED_PROPERTY_VALUE, // true
  ()=> fn.additionalProperty == ADDITIONAL_PROPERTY_VALUE, // true
  ()=> fn.constructor == ExtendedFunction, // true
  ()=> fn.constructedProperty == fn.bind().constructedProperty, // true
  ()=> fn.additionalProperty == fn.bind().additionalProperty, // true
  ()=> fn() == 0, // true
  ()=> fn(10) == 10, // true
  ()=> fn.apply({y:10}, [10]) == 20, // true
  ()=> fn.call({y:10}, 20) == 30, // true
  ()=> fn.bind({y:30})(10) == 40, // true
];

// Turn the tests / results into a printable object
let table = tests.map((test)=>(
  {test: test+'', result: test()}
));

// Print the test and result in a fancy table in the console.
// F12 much?
console.table(table);

modificare

Dato che ero dell'umore giusto , ho pensato di pubblicare un pacchetto per questo su npm.


1

Esiste una soluzione semplice che sfrutta le capacità funzionali di JavaScript: passa la "logica" come argomento della funzione al costruttore della tua classe, assegna i metodi di quella classe a quella funzione, quindi restituisci quella funzione dal costruttore come risultato :

class Funk
{
    constructor (f)
    { let proto       = Funk.prototype;
      let methodNames = Object.getOwnPropertyNames (proto);
      methodNames.map (k => f[k] = this[k]);
      return f;
    }

    methodX () {return 3}
}

let myFunk  = new Funk (x => x + 1);
let two     = myFunk(1);         // == 2
let three   = myFunk.methodX();  // == 3

Quanto sopra è stato testato su Node.js 8.

Un difetto dell'esempio sopra è che non supporta metodi ereditati dalla catena di superclassi. Per supportarlo, sostituisci semplicemente "Object. GetOwnPropertyNames (...)" con qualcosa che restituisca anche i nomi dei metodi ereditati. Come farlo credo sia spiegato in qualche altra domanda-risposta su Stack Overflow :-). BTW. Sarebbe bello se ES7 aggiungesse un metodo per produrre anche i nomi dei metodi ereditati ;-).

Se è necessario supportare i metodi ereditati, una possibilità è aggiungere un metodo statico alla classe precedente che restituisce tutti i nomi dei metodi ereditati e locali. Quindi chiamalo dal costruttore. Se poi estendi quella classe Funk, ottieni anche quel metodo statico ereditato.


Penso che questo esempio dia una risposta semplice alla domanda originale "... come posso implementare la logica per tale chiamata". Basta passarlo come argomento con valori di funzione al costruttore. Nel codice sopra la classe Funk non estende esplicitamente la funzione anche se potrebbe, non ne ha davvero bisogno. Come puoi vedere puoi chiamare le sue "istanze" sporgenti come chiami una qualsiasi funzione ordinaria.
Panu Logic
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.