javascript class inherit from Function class

允我心安 提交于 2019-11-28 07:00:23

Your trying to inherit from Function. This is a right pain to do. I suggest you do the following instead

Live Example

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

You want to create a prototype object that forms the basis of your "class". It has your generic methods/attributes. It also has a constructor that gets invoked on object construction and a call method that gets invoked when you call the function

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

A function factory takes a prototype object and returns a factory for it. The returned function when called will give you a new function object that "inherits" from your prototype object.

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

Here you create your factory and then create your instance.

However this isn't proper prototypical OO. we are just shallow copying properties of a prototype into a new object. So changes to the prototype will not reflect back to the original object.

If you want real prototypical OO then you need to use a hack.

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

Notice how we use the non-standard deprecated .__proto__ and we are mutating the value of [[Prototype]] at run-time which is considered evil.

Alex Wayne

JS does not allow a constructor to return a function, even though functions are objects. So you cant have an instantiation of a prototype that is itself executable. (Am I right in this? please correct if I'm not, it's an interesting question).

Though you could do a factory function:

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

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

You can extend Function and pass the wanted function body as String to the super constructor. The context of the function can be accessed with arguments.callee.

Example for an observable Attribute 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 = [];
    }
}

Example usage:

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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!