调用不带括号的函数

2022-08-29 23:55:09

我今天被告知,可以调用不带括号的函数。我能想到的唯一方法是使用applecallet等函数。

f.apply(this);
f.call(this);

但这些需要括号,让我们处于原点。我还考虑了将函数传递给某种事件处理程序(如 setTimeout)的想法:applycall

setTimeout(f, 500);

但问题就变成了“如何在没有括号的情况下调用?setTimeout

那么这个谜题的解决方案是什么呢?如何在不使用括号的情况下在Javascript中调用函数?


答案 1

有几种不同的方法可以调用不带括号的函数。

假设您定义了此函数:

function greet() {
    console.log('hello');
}

然后,请遵循一些不带括号的调用方法:greet

1. 作为构造函数

您可以使用不带括号调用的函数:new

new greet; // parentheses are optional in this construct.

来自MDN的新操作器

语法

new constructor[([arguments])]

2. 作为或实施toStringvalueOf

toStringvalueOf 是特殊方法:当需要转换时,它们会被隐式调用:

var obj = {
    toString: function() {
         return 'hello';
    }
}

'' + obj; // concatenation forces cast to string and call to toString.

你可以(ab)使用这个模式来调用,不带括号:greet

'' + { toString: greet };

或与 :valueOf

+{ valueOf: greet };

valueOf并且实际上是从@@toPrimitive方法调用的(从ES6开始),因此您也可以实现方法:toString

+{ [Symbol.toPrimitive]: greet }
"" + { [Symbol.toPrimitive]: greet }

2.b 在函数原型中重写valueOf

您可以采用前面的想法来覆盖函数原型上的方法:valueOf

Function.prototype.valueOf = function() {
    this.call(this);
    // Optional improvement: avoid `NaN` issues when used in expressions.
    return 0; 
};

完成此操作后,您可以编写:

+greet;

尽管下面有括号,但实际的触发调用没有括号。在博客“在JavaScript中调用方法,而不真正调用它们”中查看更多相关信息

3. 作为生成器

您可以定义一个生成器函数 (with ),它返回一个迭代器。您可以使用跨页语法for...的语法。*

首先,我们需要原始函数的生成器变体:greet

function* greet_gen() {
    console.log('hello');
}

然后我们通过定义@@iterator方法来调用它,不带括号:

[...{ [Symbol.iterator]: greet_gen }];

通常,生成器会在某个地方有一个 yield 关键字,但调用函数时不需要它。

最后一个语句调用函数,但这也可以通过解构来完成:

[,] = { [Symbol.iterator]: greet_gen };

或一个构造,但它有自己的括号:for ... of

for ({} of { [Symbol.iterator]: greet_gen });

请注意,您也可以使用原始函数执行上述操作,但是在执行(在FF和Chrome上测试)后,它将在进程中触发异常。您可以使用块来管理异常。greetgreettry...catch

4. 饰演 Getter

@jehna1对此有一个完整的答案,所以给他一个荣誉。下面是在全局作用域上调用无括号函数的方法,避免了已弃用的 __defineGetter__ 方法。它使用 Object.defineProperty 代替。

我们需要为此创建原始函数的变体:greet

Object.defineProperty(window, 'greet_get', { get: greet });

然后:

greet_get;

替换为您的全局对象。window

您可以调用原始函数,而不会在全局对象上留下跟踪,如下所示:greet

Object.defineProperty({}, 'greet', { get: greet }).greet;

但有人可能会说,我们在这里确实有括号(尽管它们不参与实际的调用)。

5. 作为标签功能

从 ES6 开始,您可以使用以下语法调用一个函数,将其传递给模板文本

greet``;

请参阅“标记的模板文本”。

6. 作为代理处理程序

从 ES6 开始,您可以定义代理

var proxy = new Proxy({}, { get: greet } );

然后读取任何属性值将调用:greet

proxy._; // even if property not defined, it still triggers greet

这有许多变体。再举一个例子:

var proxy = new Proxy({}, { has: greet } );

1 in proxy; // triggers greet

7. 作为实例检查器

该运算符在第二个操作数上执行该方法,如果定义:instanceof@@hasInstance

1 instanceof { [Symbol.hasInstance]: greet } // triggers greet

答案 2

最简单的方法是与操作员一起:new

function f() {
  alert('hello');
}

new f;

虽然这是非正统和不自然的,但它是有效的,是完全合法的。

如果未使用任何参数,则该运算符不需要括号。new