JavaScript中可调用对象的构造函数[duplicate]

问题描述 投票:11回答:4

这个问题在这里已有答案:

如何在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的属性,并且在false上说instanceof CallablePoint。是否可以为可调用对象创建工作构造函数?

javascript constructor callable
4个回答
21
投票

原来它实际上是可能的。通过使用function语法或Function构造函数创建函数时,它获取内部[[Call]]属性。它不是函数本身的属性,而是任何函数在构造时获得的属性。

虽然这只意味着任何有[[Call]]的东西在构造时可能只有Function(好吧,有一个例外 - Function.prototype本身并不从Function继承),这并不意味着它不能在以后成为别的东西,同时保留[[Call]]属性。好吧,如果您的浏览器不是IE <11。

允许改变魔法的是来自ES6的__proto__,已在许多浏览器中实现。 __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)

首先,CallablePoint的构造函数生成Function(只允许Functions以[[Call]]属性开头。接下来,我更改其原型,因此它将继承CallablePoint。此时我有一个不继承Function的函数(有点混乱)。

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

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


1
投票

假设您使用Python中的__call__功能并且通常称为“可调用对象”,我将写下我的答案。 “可调用对象”在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中的作用非常小,所以我认为你无论如何都不会错过它。但是,正如您已经发现的那样,您可以在函数上创建属性,就像在对象上一样。所以,你可以用一种不太方便的方式来做到这一点。


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

-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);
}
© www.soinside.com 2019 - 2024. All rights reserved.