你能用JavaScript创建自定义原型的函数吗?

时间:2013-05-16 19:44:43

标签: javascript function customization prototype

首先,我想要向Function.prototype添加方法。这样做会使它们可用于所有功能,而这不是我正在寻找的。

在JavaScript中,您可以使用以下自定义原型创建对象:

function CustomObj() {}
CustomObj.prototype = {};
CustomObj.prototype.sayFoo = function () { return 'foo' };

var myCustomObj = new CustomObj(); //=> returns an object: {}
myCusomObj.sayFoo(); //=> 'foo'

您还可以使用以下自定义原型创建类似数组的对象:

function CustomArr() {}
CustomArr.prototype = [];
CustomObj.prototype.sayFoo = function () { return 'foo' };

var myCustomArr = new CustomArr(); //=> returns an ordered object: []
myCustomArr.sayFoo(); //=> 'foo'

我想要做的是使用某种构造函数以相同的方式创建具有自己的自定义原型的函数。但是,以下内容不起作用:

function CustomFn() {}
CustomFn.prototype = function () {};
CustomFn.prototype.sayFoo = function () { return 'foo' };

var myCustomFn = new CustomFn(); //=> PROBLEM! returns an object: {}
myCustomFn.sayFoo(); //=> 'foo'

// ^^ Here, the prototype was applied but the output was not a function.
myCustomFn(); //=> TypeError: object is not a function

那么有什么方法可以完成我想要做的事情吗?

更新

也许还有另一种方式我可以问这个问题会让它更清晰一点。

关闭的想法存在问题:

function makeFn() {
  var output = function () { /* do some stuff */ };
  output.foo = function () { /* do some stuff */ };
  return output;
}
var specialFn = makeFn();

基本上,这种技术给了我想要的东西。但问题是,每次调用makeFn时,output.foo都必须创建为一个完全独立的函数,占用自己的内存。毛。所以我可以将该方法移出闭包:

var protoMethods = {
  "foo" : function () { /* do some stuff */ }
};
function makeFn() {
  var output = function () { /* do some stuff */ };
  for (var i in protoMethods) {
    Object.prototype.hasOwnProperty.call(protoMethods, i) &&
      (output[i] = protoMethods[i]);
  }
  return output;
}
var specialFn = makeFn();

但是现在每次调用makeFn时我都必须手动执行迭代,这比我只能将protoMethods指定为output的原型效率低。那么,有了这个新的更新,任何想法?

3 个答案:

答案 0 :(得分:3)

确实这是一个棘手的事情,如果语言设计得好的话,应该比它应该更复杂......

基本上,你不能在当前版本中干净利落地做到这一点。函数以外的对象不能被调用。

在将来的Javascript版本中,您可以使用可以定义“调用”处理程序的“代理”对象来执行此操作。但在我看来,它仍然过于复杂和人为。

另一种方法是让你的对象成为一个真正的函数,而不是一个自定义对象。然后尝试设置其__proto__,这是非标准的,但适用于大多数现代浏览器,但Opera和IE 8或更低版本除外。也可以将其constructor属性设置为伪造instanceof检查......但这样的黑客攻击非常棘手,结果会因环境而异。

以下示例适用于我的Firefox: http://jsfiddle.net/Q3422/2/

function MyFun() {
    if (!this || this==window) {
        return new MyFun();
    }

    var f = function() {
        return "thanks for calling!";
    }
    f.__proto__ = MyFun.prototype;
    f.constructor = MyFun;

    return f;
}

MyFun.prototype = {
    foo: function() {
        return "foo:" + this();
    },
    __proto__: Function.prototype
};

var f = new MyFun();
alert("proto method:"+f.foo()); // try our prototype methods
alert("function method:"+f.call()); // try standard function methods
alert("function call:"+f()); // try use as a function
alert('typeof:' + typeof f); // "function", not "object". No way around it in current js versions
alert('is MyFun:' + (f instanceof MyFun)); // true
alert('is Function:' + (f instanceof Function)); // true

只是想补充一点,你不应该担心“复制”函数到对象的每个实例。函数本身是一个对象,因此永远不会被复制,也不会被重新编译或任何东西。它不会浪费内存,除了函数对象引用本身和任何闭包变量。

迭代原型来复制它不应该关心你,我猜你不会有很多方法。

因此,如果您需要支持 proto 无法设置的环境,那么您自己的最后一个解决方案可能是最好的,并且您并不担心在创建了一些对象之后您的原型可能会被扩展可能不会接受这些变化。

答案 1 :(得分:0)

我在V library工作时也尝试过。我想重写Function构造函数来强制构造函数的受限语法,我称之为“类函数”(我有信心这样做)。

答案是否定的,使用new运算符只能创建新的“对象”,而不是新的“函数对象”。

但是你可以使用构造函数作为构造函数和函数!

var CustomFn = function () {
  if (this instanceof CustomFn) {
    // here we are 'new CustomFn()'
  }
  else {
    // here we are 'CustomFn()' or 'CustomFn.call()'
  }
};

或者我认为是更好的概念,首先要做功能,然后让构造函数去:

var CustomFn = function () {
  if (!(this instanceof CustomFn)) { // functioning
    // here we are 'CustomFn()' or 'CustomFn.call()'
    return new CustomFn(); // or return undefined or throw
  }

  // constructing
  // here we are 'new CustomFn()'

  // BaseCustomFn.call(this);
};

答案 2 :(得分:0)

你是JavaScript中继承的核心所在。是的,因为原型是对象,所以你需要将CustomFn的原型设置为对象而不是函数。

但是该对象可以来自另一个函数:

function ParentFn() {}
function CustomFn() {}
CustomFn.prototype = Object.create(ParentFn.prototype);
CustomFn.prototype.sayFoo = fun ...

如果您没有ES5或polyfill:

CustomFn.prototype = (function() {
                          function F(){}
                          F.prototype = ParentFn.prototype;
                          return new F();
                      }());

有些人可能会告诉您只需执行以下操作,但上述方法更好:

CustomFn.prototype = new ParentFn();