问题
How can I make constructor for callable object in JavaScript?
I've attempted various ways, like following. The example there is just shortened example of actual object.
function CallablePoint(x, y) {
function point() {
// Complex calculations at this point
return point
}
point.x = x
point.y = y
return point
}
This works at first, but the object it creates isn't instance of CallablePoint
, so it doesn't copy properties from CallablePoint.prototype
and says false
on instanceof CallablePoint
. Is it possible to make working constructor for callable object?
回答1:
Turns out it's actually possible. When the function is created, either by using function
syntax, or Function
constructor, it gets internal [[Call]]
property. It isn't a property of function itself, but rather property that any function gets when constructed.
While that only means that anything with [[Call]]
could be only Function
when it's constructed (well, there is one exception – Function.prototype
itself that doesn't inherit from Function
), that doesn't mean it cannot become something else later, while preserving [[Call]]
property. Well, provided your browser isn't IE < 11.
The thing that allows changing the magic would be __proto__
from ES6, already implemented in many browsers. __proto__
is a magical property that contains current prototype. By changing it, I can make function that inherits from something that isn't Function
.
function CallablePoint(x, y) {
function point() {
// Complex calculations at this point
return point
}
point.__proto__ = CallablePoint.prototype
point.x = x
point.y = y
return point
}
// CallablePoint should inherit from Function, just so you could use
// various function methods. This is not a requirement, but it's
// useful.
CallablePoint.prototype = Object.create(Function.prototype)
First, the constructor for CallablePoint
makes a Function
(only Function
s are allowed to begin with [[Call]]
property. Next, I change its prototype, so it would inherit CallablePoint
. At this point I have a function that doesn't inherit from Function
(sort of confusing).
After I defined constructor for CallablePoint
s, I set the prototype of CallablePoint
to Function
, so I have CallablePoint
that inherits from Function
.
This way, the CallablePoint
instances have prototype chain: CallablePoint -> Function -> Object
, while still being callable. Also, because the object is callable, it has according to the specification, typeof
equal to 'function'
.
回答2:
I will write my answer assuming that you were after __call__
functionality available in Python and often referred to as "callable object". "Callable object" sounds foreign in JavaScript context.
I've tried several JavaScript engines, but none of those I've tried allows you to call objects, even if you inherit from Function
. For instance:
function Callable(x) {
... "use strict";
... this.__proto__ = Function.prototype;
... this.toString = function() { return x; };
... }
undefined
> var c = new Callable(42);
var c = new Callable(42);
undefined
> c;
c;
{ toString: [function] }
> c(42);
c(42);
TypeError: Property 'c' of object #<Object> is not a function
at repl:1:1
at REPLServer.eval (repl.js:80:21)
at repl.js:190:20
at REPLServer.eval (repl.js:87:5)
at Interface.<anonymous> (repl.js:182:12)
at Interface.emit (events.js:67:17)
at Interface._onLine (readline.js:162:10)
at Interface._line (readline.js:426:8)
at Interface._ttyWrite (readline.js:603:14)
at ReadStream.<anonymous> (readline.js:82:12)
> c instanceof Function;
c instanceof Function;
true
c.apply(null, [43]);
TypeError: Function.prototype.apply was called on 43, which is a object and not a function
at Function.APPLY_PREPARE (native)
at repl:1:3
at REPLServer.eval (repl.js:80:21)
at repl.js:190:20
at REPLServer.eval (repl.js:87:5)
at Interface.<anonymous> (repl.js:182:12)
at Interface.emit (events.js:67:17)
at Interface._onLine (readline.js:162:10)
at Interface._line (readline.js:426:8)
at Interface._ttyWrite (readline.js:603:14)
>
This is V8 (Node.js). I.e. you may have an object, that formally inherits from function, but it is not callable, and I could not find a way to convince the runtime it may be called. I had similar results in Mozilla's implementation of JavaScrip, so I think it must be universal.
However, the role of custom types in JavaScript is vanishingly small, so I don't think you will be missing it that much anyway. But, as you had already discovered, you can create properties on functions, the same way as you can on objects. So, you can do it, just in a less convenient way.
回答3:
I'm not sure if you're aware that your object will only be an instance of CallablePoint
if you use the new
keyword. By naming it "callable", you make me think you don't want to use new
. Anyway, there is a way to force an instance to be returned (thanks for the tip, Resig):
function CallablePoint(x, y) {
if (this instanceof CallablePoint) {
// Your "constructor" code goes here.
// And don't return from here.
} else {
return new CallablePoint(x, y);
}
}
This will return an instance of CallablePoint
regardless of how it was called:
var obj1 = CallablePoint(1,2);
console.log(obj1 instanceof CallablePoint); // true
var obj2 = new CallablePoint(1,2);
console.log(obj2 instanceof CallablePoint); // true
回答4:
If you want the CallablePoint()
constructor to return an object of type CallablePoint, then you can do something like this where the CallablePoint object contains a point as a property of the object, but remains a CallablePoint object:
function CallablePoint(x, y) {
this.point = {};
this.point.x = x
this.point.y = y
}
or, if what you're really trying to do is to make a function that returns you an object of CallablePoint, then you can create a factor function:
function CallablePoint(x, y) {
this.point = {};
this.point.x = x
this.point.y = y
}
function makeCallablePoint(x, y) {
return new CallablePoint(x,y);
}
来源:https://stackoverflow.com/questions/12656079/constructor-for-callable-object-in-javascript