调用不带括号的函数

Posted

技术标签:

【中文标题】调用不带括号的函数【英文标题】:Invoking a function without parentheses 【发布时间】:2016-06-27 05:58:53 【问题描述】:

今天有人告诉我,可以。我能想到的唯一方法是使用applycall 之类的函数。

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

但是这些需要在applycall 上加上括号,让我们在第一格。我还考虑了将函数传递给某种事件处理程序的想法,例如setTimeout

setTimeout(f, 500);

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

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

【问题讨论】:

不想粗鲁,但我必须问:为什么? @jehna1 因为我是answering a question on how functions are invoked,当我说他们最后需要括号时得到了纠正。 太棒了!我没想到这个问题会有这么大的吸引力。也许我应该自己问这个问题:-) 这种问题让我心碎。这样的虐待和剥削能带来什么好处呢?我想知道一个有效的用例,其中<insert any solution> 在客观上比f() 更好或更有用。 @Aaron:你说没有正当理由,但是,正如Alexander O’Mara’s answer 指出的那样,人们可能会考虑删除括号是否足以阻止代码执行的问题——或者混淆的 Javascript 怎么样竞赛?当然,在尝试编写可维护的代码时,这是一个荒谬的目标,但这是一个有趣的问题。 【参考方案1】:

这是针对特定情况的example:

window.onload = funcRef;

虽然该语句实际上不是调用,但会导致未来调用

但是,我认为灰色区域可能适合这样的谜题 :)

【讨论】:

这很好,但那不是 JavaScript,它是 DOM。 @Amit,DOM 是浏览器 JS 环境的一部分,仍然是 JavaScript。 @zzzzBov 并非所有的 ECMAScript 都在 Web 浏览器中运行。或者您是使用“JavaScript”专门指将 ES 与 html DOM 结合起来而不是 Node 的人之一? @DamianYerrick,我认为 DOM 是在某些 JS 环境中可用的库,这并不比在某些环境中可用的下划线更少 JavaScript。它仍然是 JavaScript,只是不是 ECMAScript 规范中规定的部分。 @zzzzBov,"Although the DOM is often accessed using JavaScript, it is not a part of the JavaScript language. It can also be accessed by other languages."。例如,FireFox 使用 XPIDL 和 XPCOM 来实现 DOM。你指定的回调函数的调用是用 JavaScript 实现的,这不是不言而喻的。 DOM 不像其他 JavaScript 库那样是一个 API。【参考方案2】:

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

假设你已经定义了这个函数:

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

那么这里按照一些方法调用greet不带括号:

1。作为构造函数

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

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

来自MDN on the new oprator:

语法

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 ;

valueOftoString 实际上是从 @@toPrimitive 方法调用的(从 ES6 开始),所以你也可以实现 that 方法:

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

2.b 在函数原型中覆盖valueOf

您可以采用之前的想法来覆盖Function prototype 上的valueOf 方法:

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

完成后,您可以编写:

+greet;

虽然后面有括号,但实际触发的调用没有括号。在博客"Calling methods in JavaScript, without really calling them"中查看更多信息

3。作为生成器

您可以定义一个generator function(带有*),它返回一个iterator。您可以使用spread syntax 或for...of 语法调用它。

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

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

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

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

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

最后一条语句调用函数,但也可以使用destructuring:

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

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

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

请注意,您可以使用原始greet 函数执行上述操作,但它会在处理过程中触发异常,之后 greet 已经执行(在 FF 和 Chrome 上测试)。您可以使用 try...catch 块来管理异常。

4。作为吸气剂

@jehna1 对此有完整的答案,所以请给他点赞。这是一种在全局范围内调用函数无括号的方法,避免使用deprecated __defineGetter__ 方法。它改用Object.defineProperty

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

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

然后:

greet_get;

window 替换为您的全局对象。

您可以调用原始的 greet 函数而不在全局对象上留下任何痕迹,如下所示:

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

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

5。作为标签功能

从 ES6 开始,您可以使用以下语法调用传递 template literal 的函数:

greet``;

见"Tagged Template Literals"。

6。作为代理处理程序

从 ES6 开始,你可以定义一个proxy:

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

【讨论】:

这是一个非常有趣的使用valueOf的方法。 你忘了 ES6:func``; 你通过调用 eval 使用了括号 :) 在这种情况下,函数不会被执行,而是传递给调用者。 @trincot 另一种方法很快就会使用pipeline operator:'1' |> alert【参考方案3】:

最简单的方法是使用new 运算符:

function f() 
  alert('hello');


new f;

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

如果不使用参数,new 运算符不需要括号。

【讨论】:

更正,最简单的方法是在前面添加一个强制计算函数表达式的操作数。 !f 产生相同的结果,而无需实例化构造函数的实例(这需要创建新的 this 上下文)。它的性能要高得多,并且在许多流行的库(如 Bootstrap)中都有使用。 @THEtheChad - 评估表达式与执行函数不同,但如果您有不同的(更好?更令人惊讶?)调用(导致执行)函数的方法 - 发布答案! 在此类库中添加感叹号或任何其他单字符一元运算符(+-~)的要点是在最小化版本中保存一个字节不需要返回值的库。 (即!function()())通常的自调用语法是(function())()(不幸的是语法function()()不是跨浏览器)因为最顶层的自调用函数通常没有返回,它通常是目标这种字节节省技术 @THEtheChad 这是否成立?我只是在 Chrome 中尝试过,并没有执行功能。 function foo() alert("Foo!") ; 例如:!foo 返回false【参考方案4】:

您可以使用 getter 和 setter。

var h = 
  get ello () 
    alert("World");
  

只需使用以下命令运行此脚本:

h.ello  // Fires up alert "world"

编辑:

我们甚至可以进行辩论!

var h = 
  set ello (what) 
    alert("Hello " + what);
  


h.ello = "world" // Fires up alert "Hello world"

编辑 2:

您还可以定义可以不带括号运行的全局函数:

window.__defineGetter__("hello", function()  alert("world"); );
hello;  // Fires up alert "world"

还有参数:

window.__defineSetter__("hello", function(what)  alert("Hello " + what); );
hello = "world";  // Fires up alert "Hello world"

免责声明:

正如@MonkeyZeus 所说:无论你的意图多么好,你永远都不会在生产中使用这段代码。

【讨论】:

严格来说,从“我是一个挥舞着斧头的疯子,有幸接管你的代码,因为你已经转向更大更好的事情;但我知道你在哪里居住”。我真的希望这不是正常的哈哈。在您维护的插件中使用它是可以接受的。编写业务逻辑代码,请稍等我磨斧头:) @MonkeyZeus 哈哈,是的!为可以从 Google 找到此内容的未来编码人员添加了免责声明 其实这个免责声明太苛刻了。 “getter 作为函数调用”有合法的用例。事实上,它在一个非常成功的库中被广泛使用。 (您需要提示吗??:-) 请注意,__defineGetter__ 已弃用。请改用Object.defineProperty @MonkeyZeus - 正如我在评论中明确地 写的那样 - 这种函数调用风格正在被广泛使用,在一个非常成功的公共库作为 API 本身,而不是内部。【参考方案5】:

如果我们接受横向思维方法,那么在浏览器中,我们可以滥用多个 API 来执行任意 JavaScript,包括调用函数,而无需任何实际的括号字符。

1。 locationjavascript: 协议:

其中一种技术是在location 分配上滥用javascript: 协议。

工作示例:

location='javascript:alert\x281\x29'

虽然技术上在评估代码后\x28\x29 仍然是括号,但实际的() 字符不会出现。括号在 JavaScript 字符串中进行转义,在赋值时进行评估。


2。 onerroreval:

同样,根据浏览器,我们可以滥用全局onerror,将其设置为eval,并抛出一些将字符串化为有效JavaScript 的东西。这个比较棘手,因为浏览器的这种行为不一致,但这里有一个 Chrome 的例子。

Chrome 的工作示例(不是 Firefox,其他未经测试):

window.onerror=eval;Uncaught=0;throw';alert\x281\x29';

这在 Chrome 中有效,因为 throw'test' 会将 'Uncaught test' 作为第一个参数传递给 onerror,这几乎是有效的 JavaScript。如果我们改为使用throw';test',它将通过'Uncaught ;test'。现在我们有了有效的 JavaScript!只需定义Uncaught,并将测试替换为有效负载。


总结:

这样的代码真的很糟糕,并且永远不应该使用,但有时会用于 XSS 攻击,所以故事的寓意是不要依赖过滤括号以防止 XSS。使用CSP 来防止此类代码也是一个好主意。

【讨论】:

这与使用eval 并在不实际使用括号字符的情况下编写字符串不一样吗? @ZenSpitz 是的,但eval 通常需要括号,因此这个过滤器规避黑客。 可以说\x28\x29 仍然是括号。 '\x28' === '('. @trincot 这是我在答案中提到的一点,这是一种横向思维方法,它展示了可以这样做的原因。我已经在答案中说得更清楚了。 另外,投票删除此内容的人,请查看the guidelines for deleting answers。【参考方案6】:

在 ES6 中,您有所谓的标记模板文字

例如:

function foo(val) 
    console.log(val);


foo`Tagged Template Literals`;

【讨论】:

确实,这是我在您之前 1.5 年发布的答案中......不知道为什么它值得一个新的答案? 因为其他一些想要立即实现相同功能的人可以使用新答案。 @mehta-rohan,我不明白那个评论。如果这是有道理的,那么为什么不一遍又一遍地重复相同的答案呢?我看不出这会有什么用。

以上是关于调用不带括号的函数的主要内容,如果未能解决你的问题,请参考以下文章

Matlab调用不带括号的函数

Python调用函数带括号和不带括号的区别

调用不带括号的函数

调用不带括号的函数时如何传递参数[Javascript]

调用不带括号的 JS 函数? [复制]

装饰器,调用不带括号的函数?