Frage JavaScript-Klasse erbt von der Function-Klasse


Ich mag das in Javascript, ich kann eine Funktion erstellen und dann weitere Methoden und Attribute zu dieser Funktion hinzufügen

myInstance = function() {return 5}
myInstance.attr = 10

Ich möchte eine Klasse erstellen, um diese Objekte zu generieren. Ich gehe davon aus, dass ich von der Function-Basisklasse erben muss.

Mit anderen Worten, ich möchte:

var myInstance = new myFunctionClass()
var x = myInstance()
// x == 5

Aber ich weiß nicht, wie man die myFunctionClass erstellt. Ich habe folgendes versucht, aber es funktioniert nicht:

var myFunctionClass = function() {Function.call(this, "return 5")}
myFunctionClass.prototype = new Function()
myInstance = new myFunctionClass()
myInstance()
// I would hope this would return 5, but instead I get
// TypeError: Property 'myInstance' of object #<Object> is not a function

Ich habe auch die kompliziertere (und richtigere?) Vererbungsmethode ausprobiert, die hier gefunden wird: Wie erstellt man ein benutzerdefiniertes Objekt in JavaScript?mit keinem Glück mehr. Ich habe auch versucht, die util.inherits (myFunctionClass, Function) in node.js gefunden. Immer noch kein Glück

Ich habe Google erschöpft und fühle daher, dass ich etwas Grundlegendes oder Offensichtliches vermissen muss. Hilfe würde sehr geschätzt werden.


12
2017-11-02 23:26


Ursprung


Antworten:


Sie versuchen, von zu erben Function. Dies ist ein richtiger Schmerz zu tun. Ich schlage vor, dass Sie stattdessen Folgendes tun

Live Beispiel

var Proto = Object.create(Function.prototype);
Object.extend(Proto, {
  constructor: function (d) {
    console.log("construct, argument : ", d);
    this.d = d; 
    // this is your constructor logic
  },
  call: function () {
    console.log("call", this.d);
    // this get's called when you invoke the "function" that is the instance
    return "from call";
  },
  method: function () {
    console.log("method");
    // some method
    return "return from method";
  },
  // some attr
  attr: 42
});

Sie möchten ein Prototyp-Objekt erstellen, das die Basis Ihrer "Klasse" bildet. Es hat Ihre generischen Methoden / Attribute. Es hat auch einen Konstruktor, der bei der Objektkonstruktion aufgerufen wird, und eine Aufrufmethode, die aufgerufen wird, wenn Sie die Funktion aufrufen

var functionFactory = function (proto) {
  return function () {
    var f = function () {
      return f.call.apply(f, arguments);      
    };
    Object.keys(proto).forEach(function (key) {
      f[key] = proto[key];
    });
    f.constructor.apply(f, arguments);
    return f;
  }
}

Eine Funktionsfactory übernimmt ein Prototypobjekt und gibt eine Factory zurück. Die zurückgegebene Funktion gibt Ihnen beim Aufruf ein neues Funktionsobjekt, das von Ihrem Prototyp-Objekt "erbt".

var protoFactory = functionFactory(proto);
var instance = protoFactory();

Hier erstellen Sie Ihre Factory und erstellen dann Ihre Instanz.

Dies ist jedoch kein richtiges prototypisches OO. wir kopieren nur oberflächlich Eigenschaften eines Prototyps in ein neues Objekt. Änderungen am Prototyp spiegeln also nicht das ursprüngliche Objekt wider.

Wenn Sie echte prototypische OO wollen, dann müssen Sie einen Hack verwenden.

var f = function () {
  // your logic here
};
f.__proto__ = Proto;

Beachten Sie, wie wir den nicht standardmäßigen veralteten Code verwenden .__proto__  und wir mutieren den Wert von [[Prototype]] zur Laufzeit, die als böse gilt.


14
2017-11-02 23:33



JS erlaubt einem Konstruktor nicht, eine Funktion zurückzugeben, obwohl Funktionen Objekte sind. Sie können also keine Instantiierung eines Prototyps durchführen, der selbst ausführbar ist. (Habe ich Recht damit? Bitte korrigieren, wenn ich nicht bin, es ist eine interessante Frage).

Obwohl du eine Fabrikfunktion ausführen könntest:

var makeCoolFunc = function() {
  var f = function() { return 5 };
  f.a = 123;
  f.b = 'hell yes!'
  return f;
};

var func = makeCoolFunc();
var x = func();

1
2017-11-02 23:35



Sie können verlängern Function und übergeben Sie den gewünschten Funktionskörper als String an den Superkonstruktor. Auf den Kontext der Funktion kann mit zugegriffen werden arguments.callee.

Beispiel für eine beobachtbare Attributklasse:

    export default class Attribute extends Function  {

    constructor(defaultValue){
        super("value", "return arguments.callee.apply(arguments);");
        this.value = defaultValue;
        this.defaultValue = defaultValue;
        this.changeListeners = [];
    }

    apply([value]){
        if(value!==undefined){
           if(value!==this.value){
               var oldValue = this.value;
               this.value=value;
               this.changeListeners.every((changeListener)=>changeListener(oldValue, value));
           }
        }
        return this.value;
    }

    clear(){
        this.value=undefined;
    }

    reset(){
        this.value=this.defaultValue;
    }

    addChangeListener(listener){
        this.changeListeners.push(listener);
    }

    removeChangeListener(listener){
        this.changeListeners.remove(listener);
    }

    clearChangeListeners(){
        this.changeListeners = [];
    }
}

Beispielverwendung:

import Attribute from './attribute.js';

var name= new Attribute();
name('foo'); //set value of name to 'foo'

name.addChangeListener((oldValue, newValue)=>{
    alert('value changed from ' +oldValue+ ' to ' +newValue);
});
alert(name()); //show value of name: 'foo'

name('baa'); //set value of name to new value 'baa' and trigger change listener

0
2018-02-23 14:58