12

如何在 JavaScript 中为可调用对象创建构造函数?

我尝试了各种方法,例如以下方法。那里的示例只是实际对象的缩短示例。

function CallablePoint(x, y) {
    function point() {
        // Complex calculations at this point
        return point
    }
    point.x = x
    point.y = y
    return point
}

这首先有效,但它创建的对象不是 的实例CallablePoint,因此它不会复制属性CallablePoint.prototype并说falseon instanceof CallablePoint。是否可以为可调用对象创建工作构造函数?

4

4 回答 4

21

事实证明这实际上是可能的。当函数被创建时,无论是使用function语法还是Function构造函数,它都会获得内部[[Call]]属性。它不是函数本身的属性,而是任何函数在构造时获得的属性。

虽然这仅意味着任何 with[[Call]]只能Function在它被构造时才存在(嗯,有一个例外——Function.prototype它本身不继承自Function),但这并不意味着它以后不能成为其他东西,同时保留[[Call]]属性。好吧,前提是您的浏览器不是 IE < 11。

允许改变魔法的东西__proto__来自 ES6,已经在许多浏览器中实现。__proto__是一个包含当前原型的神奇属性。通过更改它,我可以创建从非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)

首先,构造函数CallablePointmake a Function(只Function允许 s 以[[Call]]属性开头。接下来,我更改了它的原型,所以它会继承CallablePoint。此时我有一个不继承自的函数Function(有点令人困惑)。

在我为 s 定义了构造函数之后CallablePoint,我将原型设置CallablePointFunction,所以我CallablePoint继承了Function

这样,CallablePoint实例具有原型链:CallablePoint -> Function -> Object,同时仍然可以调用。此外,由于该对象是可调用的,因此根据规范,它typeof等于'function'.

于 2013-06-14T14:59:14.117 回答
1

假设您追求__call__Python 中可用的功能并且通常被称为“可调用对象”,我将写下我的答案。“可调用对象”在 JavaScript 上下文中听起来很陌生。

我已经尝试了几个 JavaScript 引擎,但是我尝试过的引擎都不允许你调用对象,即使你继承自Function. 例如:

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)
> 

这是 V8 (Node.js)。即你可能有一个对象,它正式从函数继承,但它是不可调用的,我找不到一种方法来说服运行时它可以被调用。我在 Mozilla 的 JavaScrip 实现中也有类似的结果,所以我认为它必须是通用的。

然而,自定义类型在 JavaScript 中的作用微乎其微,所以我认为无论如何你都不会错过它。但是,正如您已经发现的那样,您可以在函数上创建属性,就像在对象上一样。所以,你可以做到,只是用一种不太方便的方式。

于 2012-09-29T23:04:35.587 回答
0

我不确定您是否知道CallablePoint如果您使用new关键字,您的对象将只是一个实例。通过将其命名为“可调用”,您让我认为您不想使用new. 无论如何,有一种方法可以强制返回一个实例(感谢提示,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);
    }
}

CallablePoint不管它是如何被调用的,这将返回一个实例:

var obj1 = CallablePoint(1,2);
console.log(obj1 instanceof CallablePoint); // true

var obj2 = new CallablePoint(1,2);
console.log(obj2 instanceof CallablePoint); // true
于 2012-09-29T22:40:43.410 回答
-1

如果您希望CallablePoint()构造函数返回 CallablePoint 类型的对象,那么您可以执行以下操作,其中 CallablePoint 对象包含一个点作为对象的属性,但仍然是 CallablePoint 对象:

function CallablePoint(x, y) {
    this.point = {};
    this.point.x = x
    this.point.y = y
}

或者,如果您真正想做的是创建一个返回 CallablePoint 对象的函数,那么您可以创建一个因子函数:

function CallablePoint(x, y) {
    this.point = {};
    this.point.x = x
    this.point.y = y
}

function makeCallablePoint(x, y) {
   return new CallablePoint(x,y);
}
于 2012-09-29T20:13:54.227 回答