调用没有括号的函数
我今天被告知可以调用没有括号的函数。 我能想到的唯一方法就是使用apply
或call
等函数。
f.apply(this); f.call(this);
但是,这些要求括号,并call
离开我们在广场。 我也考虑过将函数传递给setTimeout
等事件处理程序的想法:
setTimeout(f, 500);
但是这个问题变成了“你怎样在没有括号的情况下调用setTimeout
?”
那么这个谜题的解决scheme是什么? 你怎么能在Javascript中调用一个函数而不使用括号?
最简单的方法是使用new
操作符:
function f() { alert('hello'); } new f;
调用没有括号的函数有几种不同的方法。
假设你定义了这个函数:
function greet() { console.log('hello'); }
然后在这里按照一些方式来打电话greet
没有括号:
1.作为构造函数
new
你可以调用一个没有括号的函数:
new greet; // parentheses are optional in this construct.
来自MDN的new
作者 :
句法
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 };
2.b覆盖函数原型中的值
您可以采取以前的想法覆盖Function
原型上的valueOf
方法:
Function.prototype.valueOf = function() { this.call(this); // Optional improvement: avoid `NaN` issues when used in expressions. return 0; };
一旦你完成了,你可以写:
+greet;
虽然在该行中包含括号,但实际的触发调用没有括号。 在博客“在JavaScript中调用方法,而不真正调用它们”
3.作为发电机
你可以定义一个生成器函数 (带*
),它返回一个迭代器 。 您可以使用扩展语法或for...of
语法来调用它。
首先,我们需要一个原始greet
函数的生成器变体:
function* greet_gen() { console.log('hello'); }
然后我们把它叫做括号:
[...{ [Symbol.iterator]: greet_gen }];
通常情况下,发生器会在某个地方有一个yield
关键字,但是函数不需要被调用。
最后一个语句调用函数,但是也可以通过解构来完成:
[,] = { [Symbol.iterator]: greet_gen };
或一个for ... of
构造:
for ({} of { [Symbol.iterator]: greet_gen });
请注意,您也可以使用原始的greet
函数来执行上述操作,但在执行greet
(在FF和Chrome上进行testing) 之后 ,它将在此过程中触发exception。 你可以用try...catch
块pipe理exception。
像Getter一样
@ jehna1对此有一个完整的答案,所以给他信用。 这是一种在全局范围内调用无括号的函数的方法,避免了不推荐使用的__defineGetter__
方法。 它使用Object.defineProperty
来代替。
我们需要为此创build一个原始的greet
函数的变体:
Object.defineProperty(window, 'greet_get', { get: greet });
接着:
greet_get;
用你的全局对象replacewindow
。
你可以调用原始的greet
函数,而不用在这个全局对象上留下痕迹:
Object.defineProperty({}, 'greet', { get: greet }).greet;
但是有人可能会说我们在这里有括号(虽然他们没有参与实际的调用)。
5.作为标签function
有了ES6,你可以调用一个函数来传递一个模板文本 ,语法如下:
greet``;
请参阅“标记模板文字” 。 通常情况下,你不会像这里那样传递一个空的字面值,但是作为一个没有括号的调用的例子,它会这样做。
6.作为代理处理程序
在ES6中,您可以定义一个代理 :
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
你可以使用getters和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所说:不pipe你的意图多好,你永远不要在生产中使用这段代码。
以下是特定情况的示例 :
window.onload = funcRef;
虽然这个声明实际上并没有调用,但会导致未来的调用 。
但是,我想灰色的地方可能是这样的谜语:)
如果我们接受一个横向思维的方法,在浏览器中有几个API可以滥用来执行任意的JavaScript,包括调用一个函数,没有任何实际的括号字符。
1. location
和javascript:
协议:
一种这样的技术是在location
分配上滥用javascript:
协议。
工作示例:
location='javascript:alert\x281\x29'
在ES6中,你有所谓的标签模板字面值 。
例如:
function foo(val) { console.log(val); } foo`Tagged Template Literals`;