这个具有匿名闭包的React代码对validateFormat有什么作用?

Moo*_*ose 9 javascript reactjs

我正在寻找源代码React 16.4.2并注意到一些对我来说有点不熟悉的东西,并且想知道它是如何工作的.这是代码:

var validateFormat = function validateFormat(format) {};

{
  validateFormat = function validateFormat(format) {
    if (format === undefined) {
      throw new Error('invariant requires an error message argument');
    }
  };
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,有一个变量被声明被调用,validaeFormat并且它被赋予一个函数作为它的'值.这对我来说很有意义.但是,在此行之后,您可以看到该变量正被重新分配给其闭包中具有相同名称但逻辑不同的函数.

令我困惑的部分是重新分配的额外未命名的闭包.它似乎是React源代码中的常见范例.

额外牙套的目的是什么?这在运行时如何表现?

iva*_*rni 4

看来您正在查看React 构建的开发版本。

来源在这里: https: //github.com/facebook/react/blob/aeda7b745d9c080150704feb20ea576238a1b9a1/packages/shared/invariant.js

在react.development.js被转译之前,您发现的代码实际上看起来像这样。如果您不熟悉转译,请在此处快速阅读,但通常这样做是为了使用旧版浏览器可能尚不支持的语言功能(例如let箭头函数语法)。

let validateFormat = () => {};

if (__DEV__) {
  validateFormat = function(format) {
    if (format === undefined) {
      throw new Error('invariant requires an error message argument');
    }
  };
}

export default function invariant(condition, format, a, b, c, d, e, f) {
  validateFormat(format);

  if (!condition) {
    let error;
    if (format === undefined) {
      error = new Error(
        'Minified exception occurred; use the non-minified dev environment ' +
          'for the full error message and additional helpful warnings.',
      );
    } else {
      const args = [a, b, c, d, e, f];
      let argIndex = 0;
      error = new Error(
        format.replace(/%s/g, function() {
          return args[argIndex++];
        }),
      );
      error.name = 'Invariant Violation';
    }

    error.framesToPop = 1; // we don't care about invariant's own frame
    throw error;
  }
}
Run Code Online (Sandbox Code Playgroud)

注意到if (__DEV__)一点了吗?__DEV__是一个全局变量,通常设置为truefalse取决于它们正在运行的构建类型,因此在开发构建中,此代码将如下所示

let validateFormat = () => {};

if (true) {
  validateFormat = function(format) {
    if (format === undefined) {
      throw new Error('invariant requires an error message argument');
    }
  };
}
Run Code Online (Sandbox Code Playgroud)

现在,当优化该构建时,优化器将检测到此if检查是完全多余的,因为它总是评估为 true,因此它只是将其删除。任何人都猜测为什么它决定保留该{ }块,但这可能是为了不意外引入范围错误。

现在看一下React 构建的生产版本。__DEV__它将在标志设置为 的情况下生成false,然后优化器将意识到它可以删除整个 if 子句。结果是该validateFormat函数在开发中进行检查时在生产中是无操作的。React 中有很多这样的开发检查,这些检查在生产版本中被删除以节省字节。

variant.js下面是React 生产版本中该文件的代码的一些美化版本。

O = function(a, b, f, d, c, k, h, g) {
    if (!a) {
        if (void 0 === b) a = Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");
        else {
            var e = [f, d, c, k, h, g],
                l = 0;
            a = Error(b.replace(/%s/g, function() {
                return e[l++]
            }));
            a.name = "Invariant Violation"
        }
        a.framesToPop = 1;
        throw a;
    }
},
Run Code Online (Sandbox Code Playgroud)

if(!a)检查实际上是if (!condition)来自原始源代码的检查(变量名称已被缩小以节省更多字节)。您可以在源代码中看到对它的调用validateFormat()已经消失,因为整个函数只是一个无操作,因为它从未在现在删除的if (__DEV__)块中重新分配。

此策略允许 React 在开发模式下运行时打印有用的错误消息和警告,而在生产模式下跳过它们以节省网络流量,因为错误/警告对用户没有任何价值。

所以,确实如此。我希望这能让你澄清一些事情,并且我不会让你更加困惑。您发现的基本上只是构建步骤期间运行的缩小过程的剩余部分。