15

首先,我不想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,这比我可以分配protoMethodsoutput. 那么,有了这个新的更新,有什么想法吗?

4

3 回答 3

5

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

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

在未来的 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不可设置的环境,那么您自己的最后一个解决方案可能是最好的,并且您不担心在某些对象已经创建后您的原型可能会被扩展并且它们可能不会接受更改。

于 2014-06-02T14:28:25.227 回答
2

你是 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();
于 2013-05-16T20:10:03.473 回答
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);
};
于 2013-05-16T19:59:16.527 回答