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
Invoking a function without parentheses
提问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 apply
or call
.
今天有人告诉我,可以调用不带括号的函数。我能想到的唯一方法是使用像apply
or 之类的函数call
。
f.apply(this);
f.call(this);
But these require parentheses on apply
and call
leaving us at square one. I also considered the idea of passing the function to some sort of event handler such as setTimeout
:
但是这些需要括号apply
,call
让我们保持在第一个位置。我还考虑了将函数传递给某种事件处理程序的想法,例如setTimeout
:
setTimeout(f, 500);
But then the question becomes "how do you invoke setTimeout
without 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 greet
without parentheses:
那么这里遵循一些greet
不带括号的调用方式:
1. As Constructor
1. 作为构造函数
With new
you can invoke a function without parentheses:
有了new
你可以调用一个函数不使用括号:
new greet; // parentheses are optional in this construct.
From MDN on the new
oprator:
Syntax
new constructor[([arguments])]
句法
new constructor[([arguments])]
2. As toString
or valueOf
Implementation
2. 作为toString
或valueOf
实施
toString
and valueOf
are special methods: they get called implicitly when a conversion is necessary:
toString
和valueOf
是特殊方法:当需要转换时,它们会被隐式调用:
var obj = {
toString: function() {
return 'hello';
}
}
'' + obj; // concatenation forces cast to string and call to toString.
You could (ab)use this pattern to call greet
without parentheses:
您可以(ab)使用此模式在greet
不带括号的情况下进行调用:
'' + { toString: greet };
Or with valueOf
:
或与valueOf
:
+{ valueOf: greet };
valueOf
and toString
are 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 valueOf
in Function Prototype
2.bvalueOf
函数原型中的覆盖
You could take the previous idea to override the valueOf
method on the Function
prototype:
您可以采用以前的想法来覆盖原型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...of
syntax.
您可以定义一个生成器函数(with *
),它返回一个iterator。您可以使用扩展语法或语法调用它for...of
。
First we need a generator variant of the original greet
function:
首先,我们需要原始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 yield
keyword 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 ... of
construct, but it has parentheses of its own:
或一个for ... of
结构,但它有自己的括号:
for ({} of { [Symbol.iterator]: greet_gen });
Note that you cando the above with the original greet
function as well, but it will trigger an exception in the process, aftergreet
has been executed (tested on FF and Chrome). You could manage the exception with a try...catch
block.
请注意,您也可以使用原始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.defineProperty
instead.
@jehna1 对此有一个完整的答案,所以请相信他。这是一种在全局范围内调用无括号函数的方法,避免了不推荐使用的__defineGetter__
方法。它使用Object.defineProperty
代替。
We need to create a variant of the original greet
function for this:
我们需要为此创建原始greet
函数的变体:
Object.defineProperty(window, 'greet_get', { get: greet });
And then:
进而:
greet_get;
Replace window
with whatever your global object is.
替换window
为您的全局对象。
You could call the original greet
function 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 instanceof
operator executes the @@hasInstance
method 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 new
operator:
最简单的方法是使用new
运算符:
function f() {
alert('hello');
}
new f;
While that's unorthodox and unnatural, it works and is perfectly legal.
虽然这是非正统和不自然的,但它有效并且完全合法。
The new
operator 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
回答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. location
and javascript:
protocol:
1.location
和javascript:
协议:
One such technique is to abuse the javascript:
protocol on location
assignment.
一种这样的技术是滥用分配javascript:
协议location
。
Working Example:
工作示例:
location='javascript:alert\x281\x29'
Although technically\x28
and \x29
are 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. onerror
and eval
:
2.onerror
和eval
:
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`;