Mit ES6 können spezielle Objekte erweitert werden. Es ist also möglich, von der Funktion zu erben. Ein solches Objekt kann als Funktion aufgerufen werden, aber wie kann ich die Logik für einen solchen Aufruf implementieren?
class Smth extends Function {
constructor (x) {
// What should be done here
super();
}
}
(new Smth(256))() // to get 256 at this call?
Jede Klassenmethode erhält über einen Verweis auf die Klasseninstanz this
. Aber wenn es als Funktion aufgerufen wird, this
bezieht sich auf window
. Wie kann ich den Verweis auf die Klasseninstanz erhalten, wenn sie als Funktion aufgerufen wird?
super(x)
(dh weitergeben anFunction
)? Ich bin mir nicht sicher, obFunction
es tatsächlich erweitert werden kann.Error
unter anderem Probleme bei der Erweiterung .Function
einfach ein Funktionskonstruktor ist. Die Implementierung der Funktion muss an den Konstruktor übergeben werden. Wenn SieSmth
eine Implementierung nicht akzeptieren möchten , müssen Sie sie im Konstruktor bereitstellen, dsuper('function implementation here')
. H.Function
Konstruktor (Laufzeit), der sich stark von einem Funktionsausdruck (Syntax) unterscheidet.Antworten:
Der
super
Aufruf ruft denFunction
Konstruktor auf, der eine Codezeichenfolge erwartet. Wenn Sie auf Ihre Instanzdaten zugreifen möchten, können Sie diese einfach fest codieren:aber das ist nicht wirklich befriedigend. Wir wollen einen Verschluss verwenden.
Die zurückgegebene Funktion ist ein Abschluss, der auf Ihre Instanzvariablen zugreifen kann , ist möglich, aber nicht einfach. Das Gute ist, dass Sie nicht aufrufen müssen,
super
wenn Sie nicht möchten - Sie können immer nochreturn
beliebige Objekte aus Ihren ES6-Klassenkonstruktoren. In diesem Fall würden wir tunAber wir können es noch besser machen und dieses Ding abstrahieren aus
Smth
:Dies schafft zwar eine zusätzliche Indirektionsebene in der Vererbungskette, aber das ist nicht unbedingt eine schlechte Sache (Sie können sie anstelle der nativen erweitern
Function
). Wenn Sie es vermeiden möchten, verwenden SieBeachten Sie jedoch, dass
Smth
statischeFunction
Eigenschaften nicht dynamisch geerbt werden.quelle
Smth
Instanzen sindinstanceof Smth
(wie jeder erwarten würde). Sie können denObject.setPrototypeOf
Aufruf weglassen , wenn Sie diese oder eine Ihrer in Ihrer Klasse deklarierten Prototypmethoden nicht benötigen.Object.setPrototypeOf
ist kein so großes Optimierungsrisiko, solange es direkt nach dem Erstellen des Objekts durchgeführt wird. Nur wenn Sie den Prototyp eines Objekts während seiner Lebensdauer hin und her mutieren, wird es schlecht.this
undreturn
ein Objekt.Dies ist ein Ansatz zum Erstellen aufrufbarer Objekte, die korrekt auf ihre Objektmitglieder verweisen und die korrekte Vererbung beibehalten, ohne mit Prototypen herumzuspielen.
Einfach:
Erweitern Sie diese Klasse und fügen Sie eine
__call__
Methode hinzu, mehr unten ...Eine Erklärung in Code und Kommentaren:
Ansicht auf repl.it
Weitere Erklärung von
bind
:function.bind()
funktioniert ähnlichfunction.call()
und sie haben eine ähnliche Methodensignatur:fn.call(this, arg1, arg2, arg3, ...);
mehr auf mdnfn.bind(this, arg1, arg2, arg3, ...);
mehr auf mdnIn beiden Fällen definiert das erste Argument den
this
Kontext innerhalb der Funktion neu. Zusätzliche Argumente können auch an einen Wert gebunden werden. Aber wocall
sofort die Funktion mit dem gebundenen Wert nennt,bind
gibt ein „exotisches“ Funktion Objekt , das transparent das Original Wraps, mitthis
und alle Argumente vorgegeben.Wenn Sie also eine Funktion definieren, dann
bind
einige ihrer Argumente:Sie rufen die gebundene Funktion nur mit den verbleibenden Argumenten auf, ihr Kontext ist voreingestellt, in diesem Fall auf
['hello']
.quelle
bind
funktioniert (dh warum eine Instanz von zurückgegeben wirdExFunc
)?bind
gibt ein transparentes Funktionsobjekt zurück, das das aufgerufene Funktionsobjekt, das unser aufrufbares Objekt ist, nur mit demthis
Kontext-Rebound umschließt. Es wird also wirklich eine transparent verpackte Instanz von zurückgegebenExFunc
. Beitrag aktualisiert mit mehr Infos zubind
.constructor
After-bind
In zugewiesen werdenExFunc
. In Unterklassen von ExFunc sind alle Mitglieder zugänglich. Wie fürinstanceof
; in es6 werden gebundene Funktionen als exotisch bezeichnet, so dass ihr inneres Funktionieren nicht ersichtlich ist, aber ich denke, es leitet den Aufruf über an sein verpacktes Ziel weiterSymbol.hasInstance
. Es ähnelt einem Proxy, ist jedoch eine einfache Methode, um den gewünschten Effekt zu erzielen. Ihre Unterschrift ist ähnlich, nicht gleich.__call__
kann ich nicht zugreifenthis.a
oderthis.ab()
. zB repl.it/repls/FelineFinishedDesktopenvironmentSie können die Smth-Instanz in einen Proxy mit einer
apply
(und möglicherweiseconstruct
) Falle einschließen :quelle
this
ist noch eine leere Funktion (prüfennew Smth().toString()
).setPrototypeOf
und sagt nichts über Proxys aus. Aber ich denke, Proxys können genauso problematisch sein wiesetPrototypeOf
. Und ungefährtoString
kann es mit einer benutzerdefinierten Methode in beschattet werdenSmth.prototype
. Die native ist sowieso implementierungsabhängig.construct
Falle hinzufügen , um das Verhalten von anzugebennew new Smth(256)()
. Und fügen Sie benutzerdefinierte Methoden hinzu, die die nativen Methoden beschatten, die auf den Code einer Funktion zugreifen, wietoString
Bergi bemerkte.apply
Methode so implementiert ist, wie sie verwendet werden soll, oder nur eine Demonstration, und ich muss mehr Informationen darüber durchsehenProxy
undReflect
sie ordnungsgemäß verwenden.Ich nahm den Rat von Bergis Antwort und wickelte ihn in ein NPM-Modul ein .
quelle
Aktualisieren:
Leider funktioniert dies nicht ganz, da jetzt ein Funktionsobjekt anstelle einer Klasse zurückgegeben wird. Es scheint also, dass dies tatsächlich nicht möglich ist, ohne den Prototyp zu ändern. Lame.
Grundsätzlich besteht das Problem darin, dass es keine Möglichkeit gibt, den
this
Wert für denFunction
Konstruktor festzulegen. Der einzige Weg, dies wirklich zu tun, wäre, die.bind
Methode danach zu verwenden, dies ist jedoch nicht sehr klassenfreundlich.Wir könnten dies in einer Helfer-Basisklasse tun, werden jedoch
this
erst nach dem erstensuper
Aufruf verfügbar , daher ist es etwas schwierig.Arbeitsbeispiel:
(Beispiel erfordert modernen Browser oder
node --harmony
.)Grundsätzlich
ClassFunction
wird derFunction
Konstruktoraufruf durch die erweiterte Basisfunktion mit einer benutzerdefinierten Funktion umschlossen.bind
, die der Bindung beim ersten Aufruf ähnelt , diese jedoch später ermöglicht. DannClassFunction
ruft es im Konstruktor selbst die zurückgegebene Funktion auf, vonsuper
der jetzt die gebundene Funktion ist, und übergibtthis
die Einrichtung der benutzerdefinierten Bindungsfunktion.Dies ist alles ziemlich kompliziert, vermeidet jedoch das Mutieren des Prototyps, der aus Optimierungsgründen als fehlerhaft angesehen wird und Warnungen in Browserkonsolen generieren kann.
quelle
bound
verweist auf die Funktion, die Siereturn
aus dieser anonymen Klasse. Nennen Sie es einfach und beziehen Sie sich direkt darauf. Ich würde auch empfehlen, die Weitergabe von Code-Strings zu vermeiden, da dies nur ein Chaos ist (in jedem Schritt des Entwicklungsprozesses).extends
scheint nicht wirklich wie erwartet zu funktionierenFunction.isPrototypeOf(Smth)
und ist auchnew Smth instanceof Function
falsch.console.log((new Smth) instanceof Function);
isttrue
für mich in Node v5.11.0 und dem neuesten Firefox.new Smth instanceof Smth
, was mit Ihrer Lösung nicht funktioniert. Es sind auch keine Methoden fürSmth
Ihre Instanzen verfügbar - da Sie nur einen Standard zurückgebenFunction
, keinenSmth
.extend Function
macht auchnew Smth instanceof Smth
falsch.Zuerst kam ich zur Lösung mit
arguments.callee
, aber es war schrecklich.Ich habe erwartet, dass es im globalen strengen Modus kaputt geht, aber es scheint sogar dort zu funktionieren.
Es war ein schlechter Weg, weil
arguments.callee
der Code verwendet, als Zeichenfolge übergeben und seine Ausführung im nicht strengen Modus erzwungen wurde. Aber dann erschien die Idee zum Überschreibenapply
.Und der Test zeigt, dass ich dies als Funktion auf verschiedene Arten ausführen kann:
Version mit
in der Tat enthält
bind
Funktionalität:Version mit
macht
call
undapply
aufwindow
inkonsistent:Daher sollte der Scheck verschoben werden in
apply
:quelle
this
Fenster, nicht undefiniert, daher befindet sich die erstellte Funktion nicht im strengen Modus (zumindest in Chrome).Function
es nicht im strengen Modus ist. Obwohl schrecklich, ist es interessant +1. Sie würden wahrscheinlich nicht weiter eine Kette laufen können.Dies ist die Lösung, die ich ausgearbeitet habe und die alle meine Anforderungen an die Erweiterung von Funktionen erfüllt und die mir recht gut gedient hat. Die Vorteile dieser Technik sind:
ExtensibleFunction
ist der Code idiomatisch für das Erweitern einer ES6-Klasse (nein, mit vorgetäuschten Konstruktoren oder Proxys herumspielen).instanceof
/ oder gibt.constructor
die erwarteten Werte zurück..bind()
.apply()
und.call()
alle funktionieren wie erwartet. Dies erfolgt durch Überschreiben dieser Methoden, um den Kontext der "inneren" Funktion im Gegensatz zu der zu ändernExtensibleFunction
Instanz (oder ihrer Unterklasse) ..bind()
Gibt eine neue Instanz des Funktionskonstruktors zurück (sei esExtensibleFunction
oder eine Unterklasse). Es wird verwendet,Object.assign()
um sicherzustellen, dass die in der gebundenen Funktion gespeicherten Eigenschaften mit denen der Ursprungsfunktion übereinstimmen.Symbol
, was durch Module oder ein IIFE (oder eine andere übliche Technik zur Privatisierung von Referenzen) verschleiert werden kann.Und ohne weiteres der Code:
Bearbeiten
Da ich in der Stimmung war, dachte ich , ich würde ein Paket dafür auf npm veröffentlichen.
quelle
Es gibt eine einfache Lösung , die die Vorteile von JavaScript funktionalen Fähigkeiten nimmt: Übergeben Sie die „Logik“ als Funktion-Argument an den Konstruktor Ihrer Klasse, ordnen Sie die Methoden dieser Klasse auf diese Funktion, dann bringe diese Funktion aus dem Konstruktor als Ergebnis ::
Das Obige wurde auf Node.js 8 getestet.
Ein Nachteil des obigen Beispiels ist, dass es keine Methoden unterstützt, die von der Oberklassenkette geerbt wurden. Um dies zu unterstützen, ersetzen Sie einfach "Object. GetOwnPropertyNames (...)" durch etwas, das auch die Namen der geerbten Methoden zurückgibt. Wie das geht, wird meiner Meinung nach in einer anderen Frage-Antwort zu Stack Overflow erklärt :-). Übrigens. Es wäre schön, wenn ES7 eine Methode hinzufügen würde, um auch die Namen geerbter Methoden zu erzeugen ;-).
Wenn Sie geerbte Methoden unterstützen müssen, besteht eine Möglichkeit darin, der obigen Klasse eine statische Methode hinzuzufügen, die alle geerbten und lokalen Methodennamen zurückgibt. Rufen Sie das dann vom Konstruktor aus auf. Wenn Sie dann diese Klasse Funk erweitern, wird diese statische Methode ebenfalls vererbt.
quelle