今天有人告诉我,没有括号也可以调用函数。我能想到的唯一方法是使用apply或call这样的函数。

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

但是这些需要在apply和call上加上括号,让我们回到原点。我还考虑了将函数传递给某种事件处理程序,如setTimeout:

setTimeout(f, 500);

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

那么这个谜语的答案是什么呢?如何在Javascript中调用函数而不使用括号?


当前回答

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

1. 位置和javascript:协议:

其中一种技术就是在位置分配上滥用javascript:协议。

工作的例子:

地方= ' javascript: alert x281喝x29”

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


2. Onerror和eval:

类似地,根据浏览器的不同,我们可以滥用全局onerror,将其设置为eval,并抛出一些将字符串化为有效JavaScript的东西。这一点比较棘手,因为浏览器在这个行为上是不一致的,但这里有一个Chrome的例子。

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

window.onerror = eval;未捕获= 0;抛出“;警报\ x281 \ x29”;

这在Chrome中工作,因为throw'test'将通过'Uncaught test'作为onerror的第一个参数,这几乎是有效的JavaScript。如果我们抛出';test',它会通过'Uncaught;test'。现在我们有了有效的JavaScript!只需定义Uncaught,并将test替换为有效负载。


结论:

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

其他回答

在ES6中,你有所谓的标记模板文字。

例如:

函数foo(val) { console.log (val); } foo '标记模板文字';

你可以使用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所说:永远不要在生产中使用这段代码,不管你的意图有多好。

Array.constructor`alert\x28"invoke with whatever u want"\x29```;

因为数组。构造函数是一个函数对象。当Function对象被调用时,它们返回一个函数,它的主体是它得到的参数。

这是另一个例子,我传入函数1然后传入无参数的函数,它被称为。

function one() {
  console.log("one called");
}
function two() {
  return new Promise((resolve, reject) => {
    resolve();
  });
}
two().then(one);

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

让我们假设你已经定义了这个函数:

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

下面是一些不带括号调用greet的方法:

1. 作为构造函数

使用new可以调用不带括号的函数:

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

MDN对新运营商的评价:

语法 新构造函数(((参数)))

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开始),所以你也可以实现这个方法:

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

2.b重写函数原型中的valueOf

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

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

一旦你这样做了,你可以这样写:

+greet;

尽管后面有括号,但实际触发调用没有括号。更多相关内容请参见博客“在JavaScript中调用方法,而不是真正调用它们”。

3.作为发电机

您可以定义一个生成器函数(使用*),该函数返回一个迭代器。你可以使用扩展语法或者for…的语法。

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

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

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

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

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

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

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

或者a for…的,但它有自己的括号:

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

注意,你也可以用原始的greet函数来执行上面的操作,但它会在greet执行后触发一个异常(在FF和Chrome上测试)。您可以使用try…catch块。

4. 作为Getter

@jehna1对此有完整的答案,所以给他信任吧。下面是一种在全局作用域中调用圆括号less函数的方法,避免使用已弃用的__defineGetter__方法。它使用Object.defineProperty代替。

我们需要创建一个原始greet函数的变体:

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

然后:

greet_get;

将window替换为全局对象。

你可以调用原始的greet函数,而不会在全局对象上留下任何痕迹,就像这样:

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

但是有人可能会说这里确实有括号(尽管在实际调用中没有涉及到它们)。

5. As标签功能

从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

7. 作为实例检查器

instanceof操作符在第二个操作数上执行@@hasInstance方法,定义如下:

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