класс javascript наследуется от класса Function

мне нравится, что в JavaScript, я могу создать функцию, а затем добавить дополнительные методы и атрибуты, что функция

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

Я хотел бы создать класс для создания этих объектов. Я предполагаю, что я должен наследовать от базового класса функции.

другими словами, Я хотел бы:

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

но я не знаю, как создать myFunctionClass. Я пробовал следующее, Но это не работает:

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

Я также попробовал больше сложный (и более правильный?) метод наследования найден здесь:Как "правильно" создать пользовательский объект в JavaScript?, без удачи. Я также попытался использовать util.наследует(функция myFunctionClass) нашли в узел.js. Все равно не повезло

я исчерпал Google, и поэтому чувствую, что я должен пропустить что-то фундаментальное или очевидное. Помощь будет очень признательна.

3 ответов


вы пытаетесь унаследовать от Function. Это правильная боль. Я предлагаю вам сделать следующее вместо

Видео

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

вы хотите создать прототип объекта, который образует основу вашего "класса". Он имеет ваши общие методы / атрибуты. Он также имеет конструктор, который вызывается при построении объекта и метод вызова, который вызывается при вызове функции

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;
  }
}

фабрика функций принимает prototype object и возвращает для него фабрику. Возвращаемая функция при вызове даст вам новый объект функции, который" наследуется " от вашего объекта-прототипа.

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

здесь вы создаете свою фабрику, а затем создаете свой экземпляр.

однако это не правильный прототипический OO. мы просто поверхностно копируем свойства прототипа в новый объект. Таким образом, изменения прототипа не отразятся на исходном объекте.

если вы хотите реально prototypical OO тогда вам нужно использовать Хак.

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

обратите внимание, как мы используем нестандартные устаревший .__proto__ и мы мутируем значение [[Prototype]] во время выполнения, которых считается злом.


JS не позволяет конструктору возвращать функцию, даже если функции являются объектами. Таким образом, вы не можете иметь экземпляр прототипа, который сам является исполняемым. (Я прав? поправьте, если я не, это интересный вопрос).

хотя вы могли бы сделать функцию фабрики:

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

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

вы можете продлить Function и передайте тело разыскиваемой функции в виде строки супер конструктору. Контекст функции можно получить с помощью arguments.callee.

пример для наблюдаемого атрибута class:

    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 = [];
    }
}

пример использования:

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