JavaScript类继承自Function类

14

我喜欢JavaScript中的一点,就是可以创建一个函数,然后向该函数添加更多的方法和属性。

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

我想创建一个类来生成这些对象。我假设我需要从Function基类继承。

换句话说,我想要:

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中的自定义对象,但是没有更多的运气。我还尝试了在node.js中找到的util.inherits(myFunctionClass, Function)。仍然没有运气。

我已经用尽了Google,因此感觉自己必须缺少一些基本或明显的东西。非常感谢您的帮助。


在ECMAScript第3版中,使用[[prototype]]是不可能做到的。我不知道在第5版中是否有一种巧妙的方法可以实现这一点。"传统"的方法 - 例如jQuery中使用的方法 - 是从"原型"中复制单个属性,而不使用原型继承。 - user166390
3个回答

14

你试图从Function继承。这很难做到。我建议你改为执行以下操作。

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
});
你想创建一个原型对象作为你“类”的基础。它具有通用的方法和属性。它还有一个在对象构造时被调用的构造函数和一个在调用函数时被调用的调用方法。

你想创建一个原型对象作为你“类”的基础。它包含了普遍的方法和属性。同时,它也拥有一个构造函数,当对象被构建时会被调用;以及一个调用方法,当你调用函数时会被触发。

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;
  }
}
一个函数工厂接收一个原型对象并返回一个基于该原型对象的工厂。当调用返回的函数时,将会得到一个新的函数对象,该对象继承自你的原型对象。

一个函数工厂接收一个原型对象并返回一个能够生成基于该原型对象的函数的工厂。所生成的新函数对象将会“继承”自原型对象。

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

在这里,您可以创建工厂,然后创建实例。

但是这并不是正确的原型式OO。我们只是将原型的属性进行浅复制到新对象中。因此,对原型的更改不会反映回原始对象。

如果您想要真正的原型式OO,则需要使用一个hack。

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

请注意我们如何使用非标准的废弃.__proto__ 并且在运行时改变了[[Prototype]]的值,这被认为是有害的。


这几乎是我正在寻找的解决方案。但是,我如何从方法内部访问实例的属性?this不起作用;我认为在构造函数中放置that=this,然后引用that.attribute会起作用。但是,that始终指向最近创建的实例,而不是我调用该方法的实例。 - dgreisen
@dgreisen this 在方法内和构造函数内都可以使用,而在 call 方法内也可以使用。但是我不会为你调用构造函数,你必须自己调用。我已经编辑了代码,以便调用构造函数。 - Raynos
@Raynos,感谢您的回复。我的代码调用了构造函数,所以我不认为这是问题所在。问题说明:在实时示例中添加到构造函数:this.d=d;;修改调用为console.log("call", this.d);;在结尾处添加:instance.call();。instance.call()正确记录“call data”。instance()记录“call undefined”。 - dgreisen
如果我理解正确,this 指的是函数的所有者。instance.call() 的所有者是 instance -> this 指的是 instance。但是 instance() 本身的所有者是 window -> this 指向 window,而不是 instance。是否有一个关键字可以从 instance 内部指向 instance,以便我可以从 instance() 访问 instance.d? - dgreisen

1

JavaScript 不允许构造函数返回一个函数,即使函数本身也是对象。因此,您不能实例化一个可执行的原型。 (如果我说错了,请纠正我,这是一个有趣的问题)。

但是您可以使用工厂函数:

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

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

3
“JS不允许构造函数返回一个函数”- 我认为这是不正确的。你可以从构造函数中返回一个函数对象。 - Šime Vidas
4
好的,如果你返回的不是 this,那么它可能会返回值,但是返回值没有构造函数的原型,对吗?所以这没什么意义。例如:http://jsfiddle.net/mDxhg/ - Alex Wayne
2
你可以从构造函数中返回一个函数。但是,你不能返回一个具有内部 [[Call]] 属性的原型实例化对象。 - Raynos

0
您可以扩展Function并将所需的函数体作为字符串传递给其超级构造函数。函数的上下文可以使用arguments.callee访问。
可观察属性类的示例:
    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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接