JavaScript:在不使用'new'运算符的情况下使用构造函数

35
请帮我理解下面代码为什么能够工作:
<script>
    var re = RegExp('\\ba\\b') ;
    alert(re.test('a')) ;
    alert(re.test('ab')) ;
</script>

第一行没有使用 new 运算符。

据我所知,在 JavaScript 中,构造函数是用于初始化由运算符 new 创建的对象的函数,并且它们不应该返回任何东西。

2个回答

43
一般来说,如果某个文档被定义为构造函数,则使用new。但在这种情况下,当你将RegExp视为函数调用时,它具有定义好的“工厂”行为。可以查看ECMAScript(JavaScript)规范第15.10.3节(链接到旧版规范;新版规范可以从ECMA 首页(右侧)下载;我不想直接链接到一个大约4MB的PDF文件):

15.10.3 作为函数调用的RegExp构造器
15.10.3.1 RegExp(pattern, flags)
如果pattern是一个其[[Class]]属性为“RegExp”且flags未定义的对象R,则返回R。否则调用RegExp构造器(15.10.4.1),传递给它pattern和flags参数,并返回由该构造器构造的对象。

实际上,你可以定义自己的JavaScript构造函数以允许省略new关键字(通过检测它们被调用为函数而不是对象,然后正确地进行调用),但我不建议这样做,因为它会导致代码含义不清晰。(并且你不能使用class语法来完成,必须使用较老、臃肿的function语法。)

1
经过更多的测试,我发现所有工厂构造函数(String、Date等)都会出现这种行为。 - GetFree
1
我明白了。每个工厂构造函数在被作为函数调用时都有自己的行为。例如,当Date构造函数被作为函数调用时,它返回日期字符串(而不是Date对象)。 - GetFree
1
@GetFree,没错。内置构造函数通常在作为函数调用时能够正常工作,但它们的行为是不同的。例如,String将值转换为本地字符串(而不是String对象),Number将值转换为数字(而不是Number对象),等等。RegExp有点特殊,因为没有其他合理的行为方式。 - Matthew Crumley
它们的行为类似于类型转换(C++风格) - GetFree
2
通过检测它们被作为函数调用,然后转而正确地调用自己。你怎么做到的? - Sled
显示剩余3条评论

13

+1 TJ Crowder已经回答了。ECMAScript标准专门为内置构造函数在作为普通函数调用时定义了行为。通常它仅将自身作为构造函数调用,但也有一些更复杂的情况。

Javascript中的构造函数[...]不应该返回任何东西

一般来说,构造函数可以忽略this并返回一个独立的对象:

function Thing() {
    return {'foo': 1};
}

在这种情况下,你可以将函数同样用作构造函数(使用 new)或普通函数。

如果构造函数不返回任何内容,就像构造函数的通常模式一样,new 操作符本身会确保返回新创建的对象并将其传递为 this。在这种情况下,你必须使用 new

最好不要依赖于构造函数作为普通函数工作,内置构造函数的替代行为很少有用,因此通常应该坚持使用 new


1
补充一下Bob所说的:当构造函数返回值时发生的情况在规范中有所涉及(第13.2.2节)。这允许单例通过构造函数返回,除其他外(我不会说这是一个的想法,但它是可能的)。请注意,如果您返回的不是由new为您创建的对象,则很可能会破坏instanceof和原型链(除非,当然,您正在转换以前构造的另一个实例,如单例情况)。基本上,从构造函数返回任何内容都是高级用法。 :-) - T.J. Crowder
2
只是补充一下“Bob”和“Teej”的观点,如果你返回的值是数字、字符串或布尔值,那么明确的 return somethingOtherThan_this 语句将被忽略。在这种情况下,隐式的 return this 会悄悄地覆盖你自己的返回语句。 - Crescent Fresh

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