【问题标题】:Why can't I initialize the imported property without first assigning it to a variable? [duplicate]为什么我不能初始化导入的属性而不先将其分配给变量? [复制]
【发布时间】:2020-12-02 14:34:38
【问题描述】:

以下代码可以正常工作:

const Readable = require('stream').Readable;
const readStream = new Readable;

使用此代码,readStream 是一个 Readable 实例。但是,当我尝试使用以下代码消除多余的行时,它不再起作用了:

const readStream = new require('stream').Readable;

使用此代码,readStream 不是 Readable 实例,而是Readable 函数 本身。令人惊讶的是,以下代码可以正常工作:

const readStream = new require('stream').Readable();

使用此代码,readStreamReadable 实例,而不是 Readable 函数。这是我想要的行为。

那么这里发生了什么?为什么不先将函数分配给变量就不能初始化函数?如果分配给变量是必须的,那为什么最后的代码可以正常工作?

【问题讨论】:

  • 你需要写new (require('stream').Readable);

标签: javascript new-operator


【解决方案1】:

原因在于new 运算符的工作方式。 JavaScript 的一个特点是new 运算符可以带括号也可以不带括号:

// Both works because the `new` operator can be used without parentheses
const request_with_parentheses = new XMLHttpRequest();
const request_without_parentheses = new XMLHttpRequest;

它的工作方式是,直到第一对括号末尾的所有内容都成为new 表达式的一部分。如果没有括号对,则整个表达式将成为new 表达式的一部分。例子:

new Date().getMonth()
// Equivalent to:
(new Date()).getMonth()

new Date().getMonth
// Equivalent to:
(new Date()).getMonth

new Date.getMonth()
// Equivalent to:
(new Date.getMonth())

new Date.getMonth
// Equivalent to:
(new Date.getMonth)
// Which is equivalent to:
(new Date.getMonth())

顺便说一句,要注意的一点是,“括号”是指函数调用括号。我的意思不是分组括号。

所以,回到可读示例:

const readStream = new require('stream').Readable;
// Equivalent to:
const readStream = (new require('stream')).Readable;

所以,我们实际上是使用new 运算符调用“require”函数。关于new operator 工作原理的快速提醒:

  1. 创建一个空白的纯 JavaScript 对象;
  2. 通过将另一个对象设置为其父原型,将新创建的对象链接(设置其构造函数)到另一个对象;
  3. 将步骤 1 中新创建的对象作为 this 上下文传递;
  4. 如果函数没有返回对象,则返回 this。

因此,如果new 运算符所操作的函数没有返回对象,那么new 运算符就有了功能。另一方面,如果new 操作符所操作的函数已经返回了一个对象,那么new 操作符就没有任何作用。由于“require”函数已经返回了一个对象,使用new 操作符调用它没有任何效果。它本质上等同于在没有new 运算符的情况下调用它。这让我们:

const readStream = new require('stream').Readable;
// Equivalent to (because of parsing rules for the `new` operator):
const readStream = (new require('stream')).Readable;
// Equivalent to (because the `new` operator has no effect on functions that already return something)
const readStream = (require('stream')).Readable;

那么,这段代码的本质是:

  1. 导入流模块
  2. 获取对 Readable 函数的引用而不创建它的实例。

但是下面的代码呢:

const readStream = new require('stream').Readable();

这段代码确实返回了一个 Readable 实例。由于它按预期工作,因此代码必须等同于:

const Readable = require('stream').Readable;
const readStream = new Readable();

代替:

const readStream = (new require('stream')).Readable();

换句话说,由于此代码按预期工作,new 运算符必须与 第二 对括号(Readable 之后的括号)关联,而不是与第一对关联括号(require 之后的那些),对吧?这意味着new 运算符并不总是与第一对括号相关联。有时,它也可以与第二对括号相关联。我们应该如何判断new 运算符何时与第二对括号而不是与第一对括号相关联?

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

const readStream = new require('stream').Readable();

确实等价于:

const readStream = (new require('stream')).Readable();

不等同于:

const Readable = require('stream').Readable;
const readStream = new Readable();

之所以起作用,仅仅是因为Readable函数是如何实现的。换句话说,一直没有必要使用new 运算符!最初的代码可能是:

const readStream = require('stream').Readable();

【讨论】:

    猜你喜欢
    • 2023-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-16
    • 1970-01-01
    • 2016-07-11
    • 2013-09-14
    相关资源
    最近更新 更多