Javascript 调用不带括号的函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/35949554/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 18:25:09  来源:igfitidea点击:

Invoking a function without parentheses

javascript

提问by Mike Cluck

I was told today that it's possible to invoke a function without parentheses. The only ways I could think of was using functions like applyor call.

今天有人告诉我,可以调用不带括号的函数。我能想到的唯一方法是使用像applyor 之类的函数call

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

But these require parentheses on applyand callleaving us at square one. I also considered the idea of passing the function to some sort of event handler such as setTimeout:

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

setTimeout(f, 500);

But then the question becomes "how do you invoke setTimeoutwithout parentheses?"

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

So what's the solution to this riddle? How can you invoke a function in Javascript without using parentheses?

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

回答by trincot

There are several different ways to call a function without parentheses.

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

Let's assume you have this function defined:

假设你定义了这个函数:

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

Then here follow some ways to call greetwithout parentheses:

那么这里遵循一些greet不带括号的调用方式:

1. As Constructor

1. 作为构造函数

With newyou can invoke a function without parentheses:

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

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

From MDN on the newoprator:

MDN的newoprator

Syntax

new constructor[([arguments])]

句法

new constructor[([arguments])]

2. As toStringor valueOfImplementation

2. 作为toStringvalueOf实施

toStringand valueOfare special methods: they get called implicitly when a conversion is necessary:

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

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

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

You could (ab)use this pattern to call greetwithout parentheses:

您可以(ab)使用此模式在greet不带括号的情况下进行调用:

'' + { toString: greet };

Or with valueOf:

或与valueOf

+{ valueOf: greet };

valueOfand toStringare in fact called from the @@toPrimitivemethod (since ES6), and so you can also implement thatmethod:

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

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

2.b Overriding valueOfin Function Prototype

2.bvalueOf函数原型中的覆盖

You could take the previous idea to override the valueOfmethod on the Functionprototype:

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

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

Once you have done that, you can write:

完成后,您可以编写:

+greet;

And although there are parentheses involved down the line, the actual triggering invocation has no parentheses. See more about this in the blog "Calling methods in JavaScript, without really calling them"

虽然后面涉及到括号,但实际的触发调用没有括号。在博客“在 JavaScript 中调用方法,而不是真正调用它们”中查看更多相关信息

3. As Generator

3. 作为发电机

You could define a generator function(with *), which returns an iterator. You can call it using the spread syntaxor with the for...ofsyntax.

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

First we need a generator variant of the original greetfunction:

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

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

And then we call it without parentheses by defining the @@iteratormethod:

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

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

Normally generators would have a yieldkeyword somewhere, but it is not needed for the function to get called.

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

The last statement invokes the function, but that could also be done with destructuring:

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

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

or a for ... ofconstruct, but it has parentheses of its own:

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

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

Note that you cando the above with the original greetfunction as well, but it will trigger an exception in the process, aftergreethas been executed (tested on FF and Chrome). You could manage the exception with a try...catchblock.

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

4. As Getter

4. 作为吸气剂

@jehna1 has a full answer on this, so give him credit. Here is a way to call a function parentheses-lesson the global scope, avoiding the deprecated __defineGetter__method. It uses Object.definePropertyinstead.

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

We need to create a variant of the original greetfunction for this:

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

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

And then:

进而:

greet_get;

Replace windowwith whatever your global object is.

替换window为您的全局对象。

You could call the original greetfunction without leaving a trace on the global object like this:

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

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

But one could argue we do have parentheses here (although they are not involved in the actual invocation).

但是有人可能会争辩说我们在这里确实有括号(尽管它们不涉及实际调用)。

5. As Tag Function

5. 作为标签功能

Since ES6 you can call a function passing it a template literalwith this syntax:

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

greet``;

See "Tagged Template Literals".

请参阅“标记模板文字”

6. As Proxy Handler

6. 作为代理处理程序

Since ES6, you can define a proxy:

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

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

And then reading any property value will invoke greet:

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

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

There are many variations of this. One more example:

这有很多变化。再举一个例子:

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

1 in proxy; // triggers greet

7. As instance checker

7. 作为实例检查器

The instanceofoperator executes the @@hasInstancemethod on the second operand, when defined:

instanceof操作者执行@@hasInstance定义为当在所述第二操作数,方法:

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

回答by Amit

The easiest way to do that is with the newoperator:

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

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

new f;

While that's unorthodox and unnatural, it works and is perfectly legal.

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

The newoperator doesn't require parentheses if no parameters are used.

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

回答by jehna1

You can use getters and setters.

您可以使用 getter 和 setter。

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

Run this script just with:

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

h.ello  // Fires up alert "world"

Edit:

编辑:

We can even do arguments!

我们甚至可以进行辩论!

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

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

Edit 2:

编辑2:

You can also define global functions that can be run without parenthesis:

您还可以定义无需括号即可运行的全局函数:

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

And with arguments:

并有论据:

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

Disclaimer:

免责声明:

As @MonkeyZeus stated: Never ever shall you use this piece of code in production, no matter how good your intentions.

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

回答by Jonathan.Brink

Here's an examplefor a particular situation:

以下是针对特定情况的示例

window.onload = funcRef;

Although that statement is not actually invokingbut will lead to a future invocation.

尽管该语句实际上并未调用,但会导致将来的调用

But, I figure grey-areas might be ok for riddles like this :)

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

回答by Alexander O'Mara

If we accept a lateral thinking approach, in a browser there are several API's we can abuse to execute arbitrary JavaScript, including calling a function, without any actual parenthesis characters.

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

1. locationand javascript:protocol:

1.locationjavascript:协议:

One such technique is to abuse the javascript:protocol on locationassignment.

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

Working Example:

工作示例:

location='javascript:alert\x281\x29'

Although technically\x28and \x29are still parenthesis once the code is evaluated, the actual (and )character does not appear. The parentheses are escaped in a string of JavaScript which gets evaluated on assignment.

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



2. onerrorand eval:

2.onerroreval

Similarly, depending on the browser we can abuse the global onerror, by setting it to eval, and throwing something that will stringify to valid JavaScript. This one is trickier, because browsers are inconsistent in this behavior, but here's an example for Chrome.

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

Working example for Chrome (not Firefox, others untested):

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

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

This works in Chrome because throw'test'will pass 'Uncaught test'as the first argument to onerror, which is almost valid JavaScript. If we instead do throw';test'it will pass 'Uncaught ;test'. Now we have valid JavaScript! Just define Uncaught, and replace test with the payload.

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



In conclusion:

综上所述:

Such code is truly awful, and should never be used, but is sometimes used in XSS attacks, so the moral of the story is don't rely on filtering parenthesis to prevent XSS. Using a CSPto prevent such code would also be a good idea.

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

回答by Idan Dagan

In ES6, you have what's called Tagged Template Literals.

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

For example:

例如:

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

foo`Tagged Template Literals`;