Javascript 为什么可以';我是否初始化导入的属性而不首先将其分配给变量?

Javascript 为什么可以';我是否初始化导入的属性而不首先将其分配给变量?,javascript,new-operator,Javascript,New Operator,以下代码可以正常工作: const Readable=require('stream')。可读; const readStream=新的可读数据流; 使用此代码,readStream是一个Readable实例。但是,当我尝试使用以下代码消除多余的行时,它不再起作用: const readStream=new require('stream')。可读; 使用此代码,readStream不是可读的实例,而是可读的函数本身。令人惊讶的是,以下代码运行良好: const readStream=new

以下代码可以正常工作:

const Readable=require('stream')。可读;
const readStream=新的可读数据流;
使用此代码,
readStream
是一个
Readable
实例。但是,当我尝试使用以下代码消除多余的行时,它不再起作用:

const readStream=new require('stream')。可读;
使用此代码,
readStream
不是
可读的
实例,而是
可读的
函数本身。令人惊讶的是,以下代码运行良好:

const readStream=new require('stream').Readable();
使用此代码,
readStream
是一个
Readable
实例,而不是
Readable
函数。这是我想要的行为


这是怎么回事?为什么不先将函数赋给变量就不能初始化函数?如果必须为变量赋值,那么为什么最后一个代码可以正常工作?

原因在于新的
运算符的工作方式。JavaScript的一个特性是
new
操作符可以使用括号,也可以不使用括号:

//这两种方法都有效,因为“new”运算符可以在没有括号的情况下使用
const request_和_括号=new XMLHttpRequest();
不带括号的常量请求=新的XMLHttpRequest;
它的工作方式是,直到第一对括号结束的所有内容都成为
new
表达式的一部分。如果没有一对括号,则整个表达式将成为
new
表达式的一部分。示例:

newdate().getMonth())
//相当于:
(新日期()).getMonth()
新日期().getMonth
//相当于:
(新日期()).getMonth
新日期。getMonth()
//相当于:
(新日期。getMonth())
新的Date.getMonth
//相当于:
(新日期。getMonth)
//这相当于:
(新日期。getMonth())
顺便说一下,需要注意的是,所谓“括号”,我指的是函数调用括号。我不是指把括号分组

所以,回到可读的示例:

const readStream=new require('stream')。可读;
//相当于:
const readStream=(new require('stream'))。可读;
因此,我们实际上是用
new
操作符调用“require”函数。关于如何工作的快速提醒:

  • 创建一个空白的普通JavaScript对象
  • 通过将另一个对象设置为其父原型,将新创建的对象链接(设置其构造函数)到另一个对象
  • 将步骤1中新创建的对象作为此上下文传递
  • 如果函数不返回对象,则返回此值
  • 因此,如果
    new
    操作符操作的函数没有返回对象,则
    new
    操作符具有一个功能。另一方面,如果
    new
    操作符操作的函数已经返回了一个对象,那么
    new
    操作符没有任何效果。因为“require”函数已经返回了一个对象,所以用
    new
    操作符调用它没有效果。它本质上相当于在不使用
    new
    操作符的情况下调用它。这使我们想到:

    const readStream=new require('stream')。可读;
    //等效于(由于'new'运算符的解析规则):
    const readStream=(new require('stream'))。可读;
    //等价于(因为'new'运算符对已返回内容的函数没有影响)
    const readStream=(require('stream'))。可读;
    
    因此,该代码的基本功能是:

  • 导入流模块
  • 获取对可读函数的引用,而不创建其实例
  • 但是下面的代码呢

    const readStream=new require('stream').Readable();
    
    这段代码确实返回了一个可读的实例。由于其按预期工作,因此代码必须等效于:

    const Readable=require('stream')。可读;
    const readStream=new Readable();
    
    而不是:

    const readStream=(新的require('stream')).Readable();
    
    换句话说,由于此代码按预期工作,
    新的
    运算符必须与第二对括号(可读的
    之后的括号)相关联,而不是与第一对括号(需要的之后的括号)相关联,对吗?这意味着
    new
    运算符并不总是与第一对括号关联。有时,它也可以与第二对括号关联。当
    new
    操作符与第二对括号关联而不是与第一对括号关联时,我们应该如何判断

    否。
    新的
    操作符总是与第一对括号关联。它与第二对或更高的括号相关联。如果是这样的话,上面的代码是如何工作的?这是因为可读函数是如何实现的。上述代码之所以有效,是因为
    Readable
    函数返回一个新的可读实例,即使在没有新运算符的情况下调用它也是如此。换句话说,无论您如何调用
    Readable
    函数(使用或不使用
    new
    操作符),它总是返回Readable的新实例。因此,守则:

    const readStream=new require('stream').Readable();
    
    实际上相当于:

    const readStream=(新的require('stream')).Readable();
    
    这并不等同于:

    const Readable=require('stream')。可读;
    const readStream=new Readable();
    
    它工作的原因很简单,因为
    Readable
    函数是如何实现的。换句话说,一直以来,使用
    new
    操作符都是不必要的!最初的代码可能是:

    const readStream=require('