今天有人告诉我可以调用一个没有括号的函数。我能想到的唯一方法就是使用像apply或call这样的函数。
1 2
| f.apply(this);
f.call(this); |
但这些都需要在apply和call上加括号,这样我们就可以在1号方格处了。我还考虑了将函数传递给某种类型的事件处理程序(如setTimeout的想法:
号
但是问题变成了"你如何在没有括号的情况下调用setTimeout"?
那么这个谜语的答案是什么呢?如何在不使用括号的情况下调用javascript中的函数?
- 不想粗鲁,但我必须问:为什么?
- @Jehna1是因为我回答了一个关于如何调用函数的问题,当我说函数末尾需要括号时,它被纠正了。
- 太神了!我没想到这个问题会有那么大的吸引力。也许我应该自己问一下——)
- 作为一个实际案例,我们可以想象能够将javascript注入到一些上下文中,但是它们过滤掉了()字符。
- 这是一个让我心碎的问题。这样的虐待和剥削能带来什么好处?我想知道一个有效的用例,其中在客观上比f()更好或更有用。
- @Jehna1是因为OP希望javascript看起来像vb6/vba/vb.net吗?:)
- 键盘上可能有坏的(和)键。或者一个讨厌口齿不清的人!
- @Naomik没有正当理由。这整条线是一个"谜"(也是一个无益的,误导性的IMO)。
- @亚伦:你说没有合理的理由,但是,正如亚历山大·奥马拉的回答所指出的,人们可能在考虑移除括号是否足以阻止代码执行,或者模糊的javascript竞争如何?当然,在试图编写可维护的代码时,这是一个荒谬的目标,但这是一个有趣的疑问。
- 这个问题在研究XSS技术的使用和预防方面非常有趣。
- @亚伦,你怎么可能认为你知道没有正当理由?在我看来,你所能知道的就是你想不出一个。毫不奇怪,你认为没有正当理由的断言是错误的;参见Rob对XSS技术的评论,以及Alexander O'Mara的答案的结论。知道什么是可能的,什么是不可能的,对于安全方面的攻击者和防御者都是非常重要的。
- 没有"有效"的理由,这意味着可能发生的原因是滥用和剥削或意外。我坚持这是真的,但"有效"可能是一个糟糕的词的选择:我肯定站在纠正,有正当理由知道这个谜语的答案!
调用没有括号的函数有几种不同的方法。
假设您定义了此函数:
1 2 3
| function greet() {
console.log('hello');
} |
号
然后,下面用一些方法调用greet,不带括号:
1。作为建造师
使用new可以调用不带括号的函数:
1
| new greet; // parentheses are optional in this construct. |
来自new操作器上的MDN:
Syntax
1
| new constructor[([arguments])] |
号2。作为toString或valueOf的实施
toString和valueOf是特殊的方法:当需要转换时,会隐式调用它们:
1 2 3 4 5 6 7
| var obj = {
toString: function() {
return 'hello';
}
}
'' + obj; // concatenation forces cast to string and call to toString. |
。
您可以(ab)使用此模式调用greet,不带括号:
1
| '' + { toString: greet }; |
或与valueOf一起:
。2.b覆盖函数原型中的valueOf。
您可以采用前面的想法来覆盖Function原型上的valueOf方法:
1 2 3 4 5
| Function.prototype.valueOf = function() {
this.call(this);
// Optional improvement: avoid `NaN` issues when used in expressions.
return 0;
}; |
。
一旦你做到了,你就可以写:
尽管行下面有括号,但实际触发调用没有括号。在博客"在javascript中调用方法,而不真正调用它们"中了解更多有关这方面的信息。
三。作为发电机
您可以定义一个生成器函数(使用*),它返回一个迭代器。您可以使用扩展语法或使用for...of语法来调用它。
首先,我们需要一个原始greet函数的生成器变体:
1 2 3
| function* greet_gen() {
console.log('hello');
} |
。
然后我们称之为无括号:
1
| [...{ [Symbol.iterator]: greet_gen }]; |
通常,生成器在某个地方会有一个yield关键字,但不需要调用函数。
最后一条语句调用函数,但也可以使用析构函数来完成:
1
| [,] = { [Symbol.iterator]: greet_gen }; |
。
或者是for ... of结构,但它有自己的括号:
1
| for ({} of { [Symbol.iterator]: greet_gen }); |
号
请注意,您也可以使用原始的greet功能执行上述操作,但在执行greet之后(在ff和chrome上测试),它将在过程中触发异常。您可以使用try...catch块来管理异常。
4。作为吸气剂
@Jehna1对此有一个完整的答案,所以请相信他。这里有一种方法可以在全局范围内少调用函数括号,避免使用不推荐使用的__defineGetter__方法。它使用Object.defineProperty代替。
我们需要为此创建原始greet函数的变体:
1
| Object.defineProperty(window, 'greet_get', { get: greet }); |
号
然后:
号
用您的全局对象替换window。
您可以调用原始的greet函数,而不必在全局对象上留下这样的跟踪:
1
| Object.defineProperty({}, 'greet', { get: greet }).greet; |
号
但有人可能会说我们这里确实有括号(尽管它们不涉及实际调用)。
5。作为标记函数
使用ES6,可以使用以下语法调用传递模板文本的函数:
号
请参见"标记模板文本"。
6。作为代理处理程序
在ES6中,可以定义代理:
1
| var proxy = new Proxy({}, { get: greet } ); |
号
然后读取任何属性值将调用greet:
1
| proxy._; // even if property not defined, it still triggers greet |
号
这方面有很多变化。再举一个例子:
1 2 3
| var proxy = new Proxy({}, { has: greet } );
1 in proxy; // triggers greet |
号
- 这是使用valueOf的非常有趣的方法。
- 有趣。您也可以这样做:Function.prototype.toString=function(){ this.call(this); return"" };,之后您还可以使用+func调用函数。
- 是的,同样的原则。
- 你忘了ES6:func``;
- 谢谢,@ismaelmiguel,我已经加了一段。我觉得这个单子很难完成…
- 不客气。你能做的一些奇怪的事情是嵌套调用:func1`${func2`${func3`...`}`}`;这个列表几乎不完整,但它是迄今为止最好的一个。
- 你还没完成;-)
- 嗨,真的,还有很多!
- 我理解这个问题,目的是调用一个特定的(现有的)函数f。您的许多方法都可以用于此目的,但不是全部;并且所有方法都需要以不同的方式编写,以避免使用括号。例如,toString方法可以写成var greet = { toString: f }; greet + '';,或者更简洁地说,写成'' + { toString : f }。
- @Ruakh,谢谢你的评论!在这方面,这个问题确实可以解释。不管怎样,考虑到你的评论,我更新了我的答案。谢谢!
- 嘿,那邪恶的逃亡者呢?埃多克斯1〔10〕
- 您通过呼叫eval使用括号:)
- @Trincot不再是了:eval`${"setTimeout< function <> { console.log('hello') }, 200 >".replace(/</g ,"(").replace(/>/g ,")")}`;。
- 是的,但是为什么要使用eval?现在使用的解决方案是模板文本,而不是使用eval。把目标函数放在它的位置:greet` `; ,你就完成了(我的答案中的第5点)。
- 在返回语句中提到不带括号的函数也算数吗?如return hello;。
- 在这种情况下,函数不会执行,而是传递给调用方。
- @Trincot另一种方法将很快与管道运营商合作:'1' |> alert。
- 有趣,@reski!好奇这是否会成为未来的ES版本。
- 这个线程太不可思议了,当我第一次看到func`;语法时,我非常困惑。
- 我很喜欢这个"object.defineproperty(,'greet',get:greet)。greet;"但是是否可以定义一个新的语句,这样我就可以写"greet'self"并得到"hello myself"?属性没有参数。但也许还有别的办法
- @zibri,可以使用模板标记函数:greet`myself`;。
- 我知道,但我问的是另一件事…就像"问候自己"没有`
- @Zibri不是这样的,但是可以想象将"我自己"分配给一个属性/变量,并且greet函数将被设计为读取它。
- 我甚至可以这样做:object.defineproperty(window,'newCmd',set:(arg)=>console.log(arg))newCmd="hello"
- 我的意思是"让"的说法。
- 如果你以这种方式发现了一些有趣的东西,把它作为答案贴出来;-)或者问一个新的问题。
最简单的方法是使用new操作符:
1 2 3 4 5
| function f() {
alert('hello');
}
new f; |
号
虽然这是非正统和非自然的,但它是有效的,是完全合法的。
如果不使用参数,new运算符不需要括号。
- 更正,最简单的方法是在操作数前面加上一个强制计算函数表达式的操作数。!f的结果相同,而不实例化构造函数函数的实例(这需要创建一个新的此上下文)。它的性能要高得多,在许多流行的库(如引导程序)中使用。
- @计算表达式和执行函数是不同的,但是如果你有不同的表达式(更好吗?更令人惊讶?)调用(导致执行)函数的方法-发布一个答案!
- 在此类库中,在感叹号或任何其他单字符一元运算符(+、-、~前加一个字符的点是在不需要返回值的库的最小化版本中保存一个字节。(即!function(){}())通常的自调用语法是(function(){})()(不幸的是,语法function(){}()不是跨浏览器),因为最顶层的自调用函数通常没有返回的内容,所以它通常是这种字节保存技术的目标。
- @这是真的吗?我刚在Chrome中尝试过,但没有得到函数执行。例如:function foo() { alert("Foo!") };返回false。
你可以使用getter和setter。
1 2 3 4 5
| var h = {
get ello () {
alert("World");
}
} |
运行此脚本时只使用:
1
| h.ello // Fires up alert"world" |
。
编辑:
我们甚至可以争论!
1 2 3 4 5 6 7
| var h = {
set ello (what) {
alert("Hello" + what);
}
}
h.ello ="world" // Fires up alert"Hello world" |
号
编辑2:
还可以定义不带括号的全局函数:
1 2
| window.__defineGetter__("hello", function() { alert("world"); });
hello; // Fires up alert"world" |
号
有了论据:
1 2
| window.__defineSetter__("hello", function(what) { alert("Hello" + what); });
hello ="world"; // Fires up alert"Hello world" |
免责声明:
正如@monkeyzeus所说:在生产中永远不要使用这段代码,无论您的意图有多好。
- 严格地说,从"我是一个挥舞斧头的疯子,有幸接管你的代码,因为你已经转移到更大更好的事情,但我知道你住在哪里"。我真的希望这不是正常的LOL。在您维护的插件中使用它,可以接受。写业务逻辑代码,请稍等,我磨斧子:)
- @Monkeyzeus哈哈,是的!为未来的编码者添加了一个免责声明,可以从谷歌中找到这个。
- 事实上,这一免责声明过于严厉。"getter作为函数调用"有合法的使用案例。事实上,它在一个非常成功的图书馆中被广泛使用。(你想要个提示吗??)
- 请注意,__defineGetter__已弃用。用Object.defineProperty代替。
- @根据我的意见,在您的库中使用它是可以接受的。我认为它不用于业务逻辑代码中,当人员流失和重新雇用发生时,很容易将其传递给不太熟练的程序员?
- @Monkeyzeus——正如我在评论中明确指出的那样——这种类型的函数调用被广泛地和有意地使用在一个非常成功的公共库中,作为API本身,而不是内部。
- @阿米特哪一个?
- rubenmartinezjr @。- chaijs.com / BDD / API
下面是一个特定情况的例子:
1
| window.onload = funcRef; |
尽管该语句实际上不是在调用,但会导致将来的调用。
但是,我认为灰色区域对于像这样的谜语是可以的:)
- 很好,但这不是javascript,而是dom。
- @amit,dom是浏览器的JS环境的一部分,它仍然是JavaScript。
- @ZZZZBOV并非所有的EcmaScript都在Web浏览器中运行。或者您是使用"javascript"专门指将es与HTMLdom结合起来的人,而不是节点?
- @Damianyerrick,我认为在某些JS环境中,DOM是一个可用的库,这不会使它比在某些环境中可用的下划线少任何javascript。它仍然是JavaScript,只是不是ECMAScript规范中指定的部分。
- @ZZZZBOV,"虽然通常使用JavaScript访问DOM,但它不是JavaScript语言的一部分。它也可以被其他语言访问。"。例如,firefox使用xpidl和xpcom实现dom。显然,指定回调函数的调用是用JavaScript实现的。与其他JavaScript库不同,DOM不是API。
- @Trincot,我同意"dom不是其他javascript库那样的API",因为这是错误的。甚至连链接到的页面标题也证明了这一点:"文档对象模型(dom)-Web API mdn"dom是一个api,它的实现可能不在javascript中,但这不仅巩固了这个答案的要点,即可以在不需要括号的情况下执行函数。
- @zzzbov,实际上dom是一个api,我没有说它不是。但是它不像其他的javascript库那样是一个api,从这个意义上说它是纯javascript。是的,这可能表明可以不带括号地调用函数,只是调用它的是DOM,而OP则询问如何在javascript中调用函数。但我不会坚持,这可能是一个解释不同的情况。
- @zzzbov我猜你是说你可以从本地代码调用你的javascript函数,这是一个扩展;)调用domjavascript是一个扩展,它可以调用javascript,但它不是javascript。
- @Juanmendes,为什么调用domAPI javascript是一种延伸?调用fs节点的api javascript有多大的难度?这是一个用于JavaScript的API。如果只将EcmaScript规范中列出的函数计算为"javascript",您将发现自己使用的语言根本做不到什么。
- 这是一个普通的对象设置器…并且可以很容易地完成object.defineproperty(window,'newCmd',set:(arg)=>console.log(arg))newCmd="hello"
如果我们接受横向思维的方法,那么在浏览器中有几个API可以用来执行任意的javascript,包括调用函数,而不使用任何实际的括号字符。
1。
location和
javascript:协议:
其中一种技术是滥用location分配的javascript:协议。
工作示例:
1
| location='javascript:alert\x281\x29' |
虽然从技术上讲,一旦代码被评估,\x28和\x29仍然是圆括号,但实际的(和)字符不会出现。括号在一个javascript字符串中转义,该字符串在赋值时进行计算。
2。
onerror和
eval:
同样,根据浏览器的不同,我们可以通过将全局onerror设置为eval,并抛出将字符串化为有效javascript的东西来滥用它。这一点比较棘手,因为浏览器在这种行为中不一致,但下面是Chrome的一个例子。
Chrome的工作示例(不是Firefox,其他未经测试):
1
| 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括号,因此滤波器的王国的印章。
- arguably \x29仍然是\x28和括号。'\x28' === '('。
- "这是孩子trincot点覆盖在我的答案,这是一个横向思维法是其中一个展示的原因,这可能是完成的。我在使它更清晰的答案。
- 因此,这是谁评为删除,请审查指南通过您的答案。
- 让URL解码器工作在其允许你使用转义机制比其他JavaScript字符串文字的位置= JavaScript警报,29 281 %:%
在ES6中,有所谓的标记模板文本。
例如:
1 2 3 4 5
| function foo(val) {
console.log(val);
}
foo`Tagged Template Literals`; |
- 事实上,这是在一年前发布的答案为你……不知道为什么它是一个新的答案?
- 因为一些其他的人一样,谁想要实现的事,现在可以使用新的答案。
- 梅塔罗汉",我不知道如何。如果这是你需要的意义,然后重复同样的答案和增益过过吗?我可以看到这将是有用的知识。