JavaScript中的间接函数调用

26

有像这样的东西

f.call(...)
f.apply(...)

但是这里还有这个

(1, alert)('Zomg what is this????!!!11')

在这个情境中,似乎 "1" 没有太多意义,以下的方式可以正常工作:

(null, alert)('Zomg what is this????!!!11')
(1, null, alert)('Zomg what is this????!!!11')
(undefined, alert)('Zomg what is this????!!!11')

你能指出 ECMAScript 中描述该语法的具体部分吗?


3个回答

53

你只是在使用逗号运算符

这个运算符只会从左到右依次对其操作数求值,并返回第二个操作数的值,例如:

(0, 1); // 1
('foo', 'bar'); // 'bar'

在调用函数的上下文中,操作数的评估仅会获得一个值,而不是引用,这导致调用的函数内部的this值指向全局对象(或在新的ECMAScript 5严格模式下将为undefined)。
例如:
var foo = 'global.foo';

var obj = {
  foo: 'obj.foo',
  method: function () {
    return this.foo;
  }
};

obj.method();      // "obj.foo"
(1, obj.method)(); // "global.foo"

如您所见,第一个调用是直接调用,method中的this值将正确地引用obj(返回"obj.foo"),第二个调用由逗号运算符进行评估,将使this值指向全局对象(产生"global.foo")。
这种模式最近变得非常流行,可用于间接调用eval,例如在ES5严格模式下,这可能很有用,以获取对全局对象的引用,例如(假设您不在浏览器环境中,不存在window):
(function () {
  "use strict";
  var global = (function () { return this || (1,eval)("this"); })();
})();

在上述代码中,内部匿名函数将在严格模式下执行,导致该this值为undefined
现在||运算符将采用第二个操作数,即eval调用,它将在全局词法和变量环境中评估代码,这是一种间接调用。
但就个人而言,在此情况下,我更喜欢使用Function构造函数来获取全局对象。
(function () {
  "use strict";
  var global = Function('return this')();
})();

Function 构造函数创建的函数仅在其以 Use Strict Directive 开头时为严格模式,它们不像函数声明或函数表达式一样“继承”当前上下文的严格性。


感谢提供 ECMAScript 链接。 - Art
1
关于:“只会得到一个值,而不是引用”,这根本不是发生的事情。你看到不同的行为,是因为你在没有使用点操作符的情况下调用了该函数。如果您这样做:var temp = obj.method;temp();,您将看到相同的行为。 - Wayne
1
@lwburk:我是从规范的角度来说的,它会得到一个,而不是Reference,看一下逗号运算符返回的内容:“第4步。返回GetValue(rref)。”它使用了GetValue抽象操作。你发的例子确实等价,但略有不同,在那种情况下,temp是一个没有基对象引用 - Christian C. Salvadó
1
@lwburk,另外,您可以看到您的示例并不完全等同于ES5:eval('foo')是对eval直接调用,但(1, eval)('foo')不是,这种情况与.属性访问器无关。Kangax在这里很好地解释了这一点。 - Christian C. Salvadó
非常有趣。我的观点更加平凡:(1,obj.method) === obj.method // true - Wayne
类似 (1, obj.method)(); 的代码可以改写为 var tmp = obj.method; tmp();,在非严格模式下使用 this === window,在严格模式下使用 this === undefined - Caramiriel

9

这是逗号运算符,它会同时计算两个操作数,并返回第二个操作数的值。

因此,类似于(null, alert)的表达式会计算出alert函数,你可以立即使用括号调用它。

这在ECMA-262(PDF)的第11.14节中有描述。


3

逗号运算符会按顺序评估表达式。将表达式括在括号中返回最后一个表达式的值。因此,(1, alert)("hello")在功能上等同于:

1;
alert("hello");

就我个人而言,我想不出为什么要这样做。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接