调用不带括号的函数
Posted
技术标签:
【中文标题】调用不带括号的函数【英文标题】:Invoking a function without parentheses 【发布时间】:2016-06-27 05:58:53 【问题描述】:今天有人告诉我,可以。我能想到的唯一方法是使用apply
或call
之类的函数。
f.apply(this);
f.call(this);
但是这些需要在apply
和call
上加上括号,让我们在第一格。我还考虑了将函数传递给某种事件处理程序的想法,例如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。作为toString
或valueOf
实现
toString
和 valueOf
是特殊方法:当需要转换时,它们会被隐式调用:
var obj =
toString: function()
return 'hello';
'' + obj; // concatenation forces cast to string and call to toString.
你可以(ab)使用这个模式来调用greet
而不带括号:
'' + toString: greet ;
或者valueOf
:
+ valueOf: greet ;
valueOf
和 toString
实际上是从 @@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。 location
和 javascript:
协议:
其中一种技术是在location
分配上滥用javascript:
协议。
工作示例:
location='javascript:alert\x281\x29'
虽然技术上在评估代码后\x28
和\x29
仍然是括号,但实际的(
和)
字符不会出现。括号在 JavaScript 字符串中进行转义,在赋值时进行评估。
2。 onerror
和 eval
:
同样,根据浏览器,我们可以滥用全局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,我不明白那个评论。如果这是有道理的,那么为什么不一遍又一遍地重复相同的答案呢?我看不出这会有什么用。以上是关于调用不带括号的函数的主要内容,如果未能解决你的问题,请参考以下文章