【问题标题】:javascript class inherit from Function classjavascript 类继承自 Function 类
【发布时间】:2011-12-20 18:34:54
【问题描述】:

我喜欢在 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

我还尝试了这里找到的更复杂(也更合适?)的继承方法:How to "properly" create a custom object in JavaScript?,但没有更多的运气。我还尝试使用 node.js 中的 util.inherits(myFunctionClass, Function)。还是没有运气

我已经用尽了 Google,因此觉得我一定遗漏了一些基本的或明显的东西。非常感谢您的帮助。

【问题讨论】:

  • 在 ECMAScript 第 3 版中无法执行此操作(使用 [[prototype]])。我不知道第 5 版中是否有一个棘手的方法允许这样做。 “常规”方法——例如在 jQuery 中使用 - 从“原型”复制各个属性,而不使用原型继承。

标签: javascript inheritance prototypal-inheritance function-object


【解决方案1】:

您尝试从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 this 在方法和构造函数内部工作 this 工作,在 call 方法内部工作 this 工作。但是我不会为你调用构造函数,你必须自己做。我已经编辑了代码,所以构造函数被调用
  • @Raynos,感谢您的回复。我的代码调用了构造函数,所以我认为这不是问题。问题说明:在实例中添加到构造函数:this.d=d;;修改对console.log("call", this.d);的调用;在末尾添加:instance.call();。 instance.call() 正确记录“通话数据”。 instance() 记录“调用未定义”。
  • 如果我理解正确,this 指的是函数的所有者。 instance.call() 的所有者是 instance -> this 指的是实例。但是 instance() 本身的所有者是 window -> this 指向的是 window,而不是 instance。是否有一个关键字从实例中指向实例,所以我可以从 instance() 访问 instance.d?
  • 这正是我所需要的。谢谢。
【解决方案2】:

JS 不允许构造函数返回函数,即使函数是对象。所以你不能有一个本身是可执行的原型的实例化。 (我是对的吗?如果我不是,请纠正,这是一个有趣的问题)。

虽然你可以做一个工厂功能:

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

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

【讨论】:

  • "JS 不允许构造函数返回函数" - 我认为这是不正确的。您可以从构造函数返回一个函数对象。
  • 好吧,它可能会返回它,但如果你返回 this 以外的东西,那么返回值不会有构造函数原型,对吧?所以没有太大意义。示例:jsfiddle.net/mDxhg
  • 您可以从构造函数返回一个函数。但是,您不能返回具有内部 [[Call]] 属性的原型的实例化
【解决方案3】:

您可以扩展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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-11-27
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 2015-04-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多