First of all, I don't want to add methods to Function.prototype
. Doing that would make them available for all functions and that's not what I'm looking for.
首先,我不想为函数添加方法。这样做可以使它们适用于所有的函数,这不是我想要的。
In JavaScript you can create objects with custom prototypes like this:
在JavaScript中,你可以创建具有自定义原型的对象,比如:
function CustomObj() {}
CustomObj.prototype = {};
CustomObj.prototype.sayFoo = function () { return 'foo' };
var myCustomObj = new CustomObj(); //=> returns an object: {}
myCusomObj.sayFoo(); //=> 'foo'
You can also create array-like objects with custom prototypes like this:
你也可以用像这样的自定义原型创建类数组的对象:
function CustomArr() {}
CustomArr.prototype = [];
CustomObj.prototype.sayFoo = function () { return 'foo' };
var myCustomArr = new CustomArr(); //=> returns an ordered object: []
myCustomArr.sayFoo(); //=> 'foo'
What I'd like to do is use some kind of constructor to create a function with its own custom prototype in the same way. However, the following does not work:
我想做的是使用某种构造函数以同样的方式创建一个具有自定义原型的函数。但是,以下方法并不适用:
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
So is there any way to accomplish what I'm trying to do?
有什么方法可以实现我的目标吗?
UPDATE
Maybe there's another way I could be asking this question that would make it a little clearer.
或许我可以用另一种方式来问这个问题,让问题更清楚一些。
There's a problem with the idea of a closure:
关闭的想法有一个问题:
function makeFn() {
var output = function () { /* do some stuff */ };
output.foo = function () { /* do some stuff */ };
return output;
}
var specialFn = makeFn();
Essentially, this technique gives me what I want. However, the problem is that every time I call makeFn
, output.foo
has to be created as a totally independent function that takes up its own memory. Gross. So I could move that method out of the closure:
本质上,这个技巧给了我想要的。但是,问题是每次我调用makeFn时,输出。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();
But now I have to manually do an iteration every time I call makeFn
which would be less efficient than if I could just assign protoMethods
to be the prototype of output
. So, with this new update, any ideas?
但是现在我每次调用makeFn时都要手动进行一次迭代,这比我将原始指令赋值为输出的原型要低效率。那么,有了这个新的更新,有什么想法吗?
3 个解决方案
#1
3
It is a tricky thing indeed, more complicated than it should be if the language was designed well...
这确实是一件棘手的事情,如果语言设计得很好,那就更复杂了……
Basically, you just can't do it cleanly in current versions. Objects other than functions can not be callable.
基本上,在当前的版本中,您不能干净地完成它。不能调用函数之外的对象。
In future Javascript versions, you can do it with a "proxy" object that can define a "call" handler. But it is still way too complicated and contrived in my opinion.
在未来的Javascript版本中,您可以使用一个“代理”对象来完成,该对象可以定义一个“调用”处理程序。但在我看来,它仍然过于复杂和做作。
Another way to go about it is to make your object a real function, not a custom object. Then try to set its __proto__
, which is non-standard yet but works in most modern browsers, except Opera and IE 8 or less. Also maybe set its constructor
property for faking instanceof
checks... such hacks are quite tricky though and results will vary a lot with environments.
另一种方法是让你的对象成为一个真正的函数,而不是一个自定义对象。然后尝试设置它的__proto__,这不是标准的,但是在大多数现代浏览器中都可以使用,除了Opera和IE 8或更少。还可以为伪造instanceof检查设置它的构造函数属性……尽管如此,这样的攻击是相当棘手的,而且结果会随着环境的变化而变化。
The following example works fine on my Firefox: http://jsfiddle.net/Q3422/2/
下面的示例在我的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
Just wanted to add that you should not be worried about "copying" functions to each instance of your objects. The function itself is an object, so is never really copied, nor is it recompiled or anything. It does not waste memory, except for the function object reference itself and any closure variables.
只是想补充一点,您不应该担心将函数“复制”到对象的每个实例。函数本身是一个对象,因此不会被真正复制,也不会被重新编译或其他任何东西。它不会浪费内存,除了函数对象引用本身和任何闭包变量。
Iterating over the prototype to copy it should not concern you as well, I guess you will not have a gazillion methods.
遍历原型以复制它也不应该让您担心,我猜您不会有大量的方法。
So your own last solution is probably the best if you need to support environments where proto is not settable, and you are not worried that your prototype might get extended after some objects already got created and they may not pick up the changes.
因此,如果您需要支持无法设置proto的环境,您自己的最后一个解决方案可能是最好的,而且您也不担心在创建了一些对象之后,您的原型可能会被扩展,而这些对象可能不会接受更改。
#2
0
You're at the heart of what inheritance in JavaScript is all about. Yes, since prototypes are objects, you'll want to set the prototype of CustomFn to an object instead of a function.
JavaScript继承的核心是什么。是的,因为原型是对象,所以您需要将CustomFn的原型设置为对象,而不是函数。
But that object can come from another function:
但是这个对象可以来自另一个函数:
function ParentFn() {}
function CustomFn() {}
CustomFn.prototype = Object.create(ParentFn.prototype);
CustomFn.prototype.sayFoo = fun ...
If you don't have ES5 or a polyfill:
如果你没有ES5或polyfill:
CustomFn.prototype = (function() {
function F(){}
F.prototype = ParentFn.prototype;
return new F();
}());
Some may tell you just to do the following but the above way is better:
有些人可能会告诉你做以下的事情,但以上的方法更好:
CustomFn.prototype = new ParentFn();
#3
0
I tried that too, when working on V library. I wanted to override the Function constructor to enforce a restricted syntax of constructor functions, that I'm calling "class functions" (and I'm confident to do so).
我在V库工作时也尝试过。我想重写函数构造函数,以强制构造函数的受限制的语法,我称之为“类函数”(我有信心这样做)。
Answer is no, using the new operator you can only create new "object"s, but not new "function object"s.
答案是否定的,使用新的运算符只能创建新的“对象”,而不能创建新的“函数对象”。
However you can use a constructor function both as a constructor and as a function!
但是,您可以将构造函数用作构造函数和函数!
var CustomFn = function () {
if (this instanceof CustomFn) {
// here we are 'new CustomFn()'
}
else {
// here we are 'CustomFn()' or 'CustomFn.call()'
}
};
Or as I believe is the better concept, to do the function in first place and then let the constructor go:
或者我认为更好的概念是,先做函数,然后让构造函数运行:
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);
};
#1
3
It is a tricky thing indeed, more complicated than it should be if the language was designed well...
这确实是一件棘手的事情,如果语言设计得很好,那就更复杂了……
Basically, you just can't do it cleanly in current versions. Objects other than functions can not be callable.
基本上,在当前的版本中,您不能干净地完成它。不能调用函数之外的对象。
In future Javascript versions, you can do it with a "proxy" object that can define a "call" handler. But it is still way too complicated and contrived in my opinion.
在未来的Javascript版本中,您可以使用一个“代理”对象来完成,该对象可以定义一个“调用”处理程序。但在我看来,它仍然过于复杂和做作。
Another way to go about it is to make your object a real function, not a custom object. Then try to set its __proto__
, which is non-standard yet but works in most modern browsers, except Opera and IE 8 or less. Also maybe set its constructor
property for faking instanceof
checks... such hacks are quite tricky though and results will vary a lot with environments.
另一种方法是让你的对象成为一个真正的函数,而不是一个自定义对象。然后尝试设置它的__proto__,这不是标准的,但是在大多数现代浏览器中都可以使用,除了Opera和IE 8或更少。还可以为伪造instanceof检查设置它的构造函数属性……尽管如此,这样的攻击是相当棘手的,而且结果会随着环境的变化而变化。
The following example works fine on my Firefox: http://jsfiddle.net/Q3422/2/
下面的示例在我的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
Just wanted to add that you should not be worried about "copying" functions to each instance of your objects. The function itself is an object, so is never really copied, nor is it recompiled or anything. It does not waste memory, except for the function object reference itself and any closure variables.
只是想补充一点,您不应该担心将函数“复制”到对象的每个实例。函数本身是一个对象,因此不会被真正复制,也不会被重新编译或其他任何东西。它不会浪费内存,除了函数对象引用本身和任何闭包变量。
Iterating over the prototype to copy it should not concern you as well, I guess you will not have a gazillion methods.
遍历原型以复制它也不应该让您担心,我猜您不会有大量的方法。
So your own last solution is probably the best if you need to support environments where proto is not settable, and you are not worried that your prototype might get extended after some objects already got created and they may not pick up the changes.
因此,如果您需要支持无法设置proto的环境,您自己的最后一个解决方案可能是最好的,而且您也不担心在创建了一些对象之后,您的原型可能会被扩展,而这些对象可能不会接受更改。
#2
0
You're at the heart of what inheritance in JavaScript is all about. Yes, since prototypes are objects, you'll want to set the prototype of CustomFn to an object instead of a function.
JavaScript继承的核心是什么。是的,因为原型是对象,所以您需要将CustomFn的原型设置为对象,而不是函数。
But that object can come from another function:
但是这个对象可以来自另一个函数:
function ParentFn() {}
function CustomFn() {}
CustomFn.prototype = Object.create(ParentFn.prototype);
CustomFn.prototype.sayFoo = fun ...
If you don't have ES5 or a polyfill:
如果你没有ES5或polyfill:
CustomFn.prototype = (function() {
function F(){}
F.prototype = ParentFn.prototype;
return new F();
}());
Some may tell you just to do the following but the above way is better:
有些人可能会告诉你做以下的事情,但以上的方法更好:
CustomFn.prototype = new ParentFn();
#3
0
I tried that too, when working on V library. I wanted to override the Function constructor to enforce a restricted syntax of constructor functions, that I'm calling "class functions" (and I'm confident to do so).
我在V库工作时也尝试过。我想重写函数构造函数,以强制构造函数的受限制的语法,我称之为“类函数”(我有信心这样做)。
Answer is no, using the new operator you can only create new "object"s, but not new "function object"s.
答案是否定的,使用新的运算符只能创建新的“对象”,而不能创建新的“函数对象”。
However you can use a constructor function both as a constructor and as a function!
但是,您可以将构造函数用作构造函数和函数!
var CustomFn = function () {
if (this instanceof CustomFn) {
// here we are 'new CustomFn()'
}
else {
// here we are 'CustomFn()' or 'CustomFn.call()'
}
};
Or as I believe is the better concept, to do the function in first place and then let the constructor go:
或者我认为更好的概念是,先做函数,然后让构造函数运行:
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);
};