调用没有括号的函数

Mik*_*uck 260 javascript

今天我被告知可以调用没有括号的函数.我能想到的唯一方法是使用像apply或者这样的函数call.

f.apply(this);
f.call(this);
Run Code Online (Sandbox Code Playgroud)

但这些需要括号apply并将call我们留在第一个方位.我还考虑了将函数传递给某种事件处理程序的想法,例如setTimeout:

setTimeout(f, 500);
Run Code Online (Sandbox Code Playgroud)

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

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

tri*_*cot 400

调用没有括号的函数有几种不同的方法.

我们假设你定义了这个函数:

function greet() {
    console.log('hello');
}
Run Code Online (Sandbox Code Playgroud)

然后在这里按照一些方法调用greet没有括号:

1.作为构造函数

有了new你可以调用一个函数不使用括号:

new greet; // parentheses are optional in this construct.
Run Code Online (Sandbox Code Playgroud)

来自MDN的new鸦片:

句法

new constructor[([arguments])]
Run Code Online (Sandbox Code Playgroud)

2.作为toStringvalueOf实施

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

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

'' + obj; // concatenation forces cast to string and call to toString.
Run Code Online (Sandbox Code Playgroud)

你可以(ab)使用这个模式来调用greet没有括号:

'' + { toString: greet };
Run Code Online (Sandbox Code Playgroud)

或者valueOf:

+{ valueOf: greet };
Run Code Online (Sandbox Code Playgroud)

2.b覆盖valueOf函数原型

您可以采用先前的想法覆盖原型valueOf上的方法:Function

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

完成后,您可以写:

+greet;
Run Code Online (Sandbox Code Playgroud)

虽然在线下有括号,但实际的触发调用没有括号.在博客"使用JavaScript调用方法,而不是真正调用它们"中查看更多相关信息

3.作为发电机

您可以定义一个生成器函数(with *),它返回一个迭代器.您可以使用扩展语法语法来调用它for...of.

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

function* greet_gen() {
    console.log('hello');
}
Run Code Online (Sandbox Code Playgroud)

然后我们称之为没有括号:

[...{ [Symbol.iterator]: greet_gen }];
Run Code Online (Sandbox Code Playgroud)

通常情况下,生成器会在yield某处使用关键字,但调用该函数不需要它.

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

[,] = { [Symbol.iterator]: greet_gen };
Run Code Online (Sandbox Code Playgroud)

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

for ({} of { [Symbol.iterator]: greet_gen });
Run Code Online (Sandbox Code Playgroud)

请注意,您也可以使用原始greet功能执行上述操作,但在执行完后 greet(在FF和Chrome上测试),它将在此过程中触发异常.您可以使用try...catch块来管理异常.

作为Getter

@ jehna1对此有完整的答案,所以给他信任.这是一种在全局范围调用函数括号的方法,避免使用不推荐的__defineGetter__方法.它用来Object.defineProperty代替.

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

Object.defineProperty(window, 'greet_get', { get: greet });
Run Code Online (Sandbox Code Playgroud)

然后:

greet_get;
Run Code Online (Sandbox Code Playgroud)

替换window为您的全局对象.

您可以调用原始greet函数,而不会像这样在全局对象上留下痕迹:

Object.defineProperty({}, 'greet', { get: greet }).greet;
Run Code Online (Sandbox Code Playgroud)

但有人可能会说我们在这里有括号(虽然他们没有参与实际的调用).

5.作为标记功能

使用ES6,您可以使用以下语法调用函数传递模板文字:

greet``;
Run Code Online (Sandbox Code Playgroud)

请参阅"标记模板文字".

6.作为代理处理程序

在ES6中,您可以定义代理:

var proxy = new Proxy({}, { get: greet } );
Run Code Online (Sandbox Code Playgroud)

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

proxy._; // even if property not defined, it still triggers greet
Run Code Online (Sandbox Code Playgroud)

这有很多变化.还有一个例子:

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

1 in proxy; // triggers greet
Run Code Online (Sandbox Code Playgroud)

  • [你还没完](http://blog.keithcirkel.co.uk/metaprogramming-in-es6-symbols/) ;-) (4认同)
  • 你忘记了ES6:```func``;``` (2认同)
  • @trincot [管道操作员](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Pipeline_operator)将很快实现另一种方法:`'1' |> alert` (2认同)

Ami*_*mit 220

最简单的方法是与new运营商:

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

new f;
Run Code Online (Sandbox Code Playgroud)

虽然这是非正统和不自然的,但它起作用并且完全合法.

new如果不使用参数,运营商不需要括号.

  • 更正,最简单的方法是在操作数前加上强制要求计算函数表达式的操作数.`!f`导致相同的事情,没有实例化构造函数的实例(这需要创建一个新的这个上下文).它的性能更高,并且在许多流行的库(如Bootstrap)中使用. (6认同)
  • @THEtheChad - 评估一个表达式与执行一个函数不同,但如果你有一个不同的(更好的?更令人惊讶的?)方法来调用(导致执行)一个函数 - 发一个答案! (6认同)
  • @THEtheChad 这是真的吗?我刚刚在 Chrome 中尝试过,但没有得到函数执行。`function foo() {alert("Foo!") };` 例如:`!foo` 返回 `false` (3认同)

jeh*_*na1 93

你可以使用getter和setter.

var h = {
  get ello () {
    alert("World");
  }
}
Run Code Online (Sandbox Code Playgroud)

运行此脚本只需:

h.ello  // Fires up alert "world"
Run Code Online (Sandbox Code Playgroud)

编辑:

我们甚至可以做论点!

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

h.ello = "world" // Fires up alert "Hello world"
Run Code Online (Sandbox Code Playgroud)

编辑2:

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

window.__defineGetter__("hello", function() { alert("world"); });
hello;  // Fires up alert "world"
Run Code Online (Sandbox Code Playgroud)

并有论据:

window.__defineSetter__("hello", function(what) { alert("Hello " + what); });
hello = "world";  // Fires up alert "Hello world"
Run Code Online (Sandbox Code Playgroud)

免责声明:

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

  • 从POV中严格来说,"我是一个挥舞着疯子的斧头,有幸接管你的代码,因为你已经转向更大更好的东西;但我知道你住在哪里".我真的希望这不是正常的哈哈.在您维护的插件中使用它,可以接受.编写业务逻辑代码,请在我磨斧时抓住:) (9认同)
  • @MonkeyZeus哈哈,是的!为未来的编码人员添加了免责声明,可以从Google找到 (3认同)
  • @MonkeyZeus - 因为我**明确在评论中写道 - 这种风格的函数调用的*被*使用,广泛和故意,在一个非常成功的公共图书馆作为API本身,而不是内部. (2认同)

Jon*_*ink 23

以下是特定情况的示例:

window.onload = funcRef;
Run Code Online (Sandbox Code Playgroud)

虽然该语句实际上并未调用,但会导致将来的调用.

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

  • @DamianYerrick,我认为DOM是一个在某些JS环境中可用的库,它不会使JavaScript比在某些环境中可用的下划线更少.它仍然是JavaScript,它不是ECMAScript规范中的一部分. (9认同)
  • 这很好,但那不是JavaScript,它是DOM. (6认同)
  • @Amit,DOM是浏览器JS环境的一部分,它仍然是JavaScript. (6认同)
  • @zzzzBov并非所有ECMAScript都在Web浏览器中运行.或者你是那些使用"JavaScript"专门提到将ES与HTML DOM结合起来而不是Node的人? (3认同)
  • @zzzzBov,["虽然经常使用JavaScript访问DOM,但它不是JavaScript语言的一部分.它也可以被其他语言访问."](https://developer.mozilla.org/en-US/文档/网络/ API/Document_Object_Model).例如,FireFox使用XPIDL和XPCOM进行DOM实现.在JavaScript中实现指定回调函数的调用并不是那么不言自明.DOM不像其他JavaScript库那样是API. (3认同)

Ale*_*ara 15

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

1. locationjavascript:协议:

一种这样的技术是javascript:location分配时滥用协议.

工作实例:

location='javascript:alert\x281\x29'
Run Code Online (Sandbox Code Playgroud)

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


2. onerroreval:

同样,根据浏览器的不同,我们可以onerror通过设置它来滥用全局eval,并抛出将字符串化为有效JavaScript的内容.这个比较棘手,因为浏览器在这种行为上不一致,但这是Chrome的一个例子.

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

window.onerror=eval;Uncaught=0;throw';alert\x281\x29';
Run Code Online (Sandbox Code Playgroud)

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


结论:

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

  • 可以说`\ x28`和`\ x29`仍然是括号.`'\ x28'==='('` (5认同)

Ida*_*gan 5

在 ES6 中,您拥有所谓的Tagged Template Literals

例如:

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

foo`Tagged Template Literals`;
Run Code Online (Sandbox Code Playgroud)

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