代码之家  ›  专栏  ›  技术社区  ›  Moose

这个带有匿名闭包的反应代码对validateFormat做了什么?

  •  9
  • Moose  · 技术社区  · 6 年前

    我正在寻找 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');
        }
      };
    }
    

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

    让我困惑的部分是围绕重新分配的附加未命名闭包。在React的源代码中,这似乎是一个常见的范例。

    额外的牙套有什么作用?这在运行时表现如何?

    1 回复  |  直到 6 年前
        1
  •  2
  •   ivarni    6 年前

    你好像在看 development version 反应构造。

    消息来源如下: https://github.com/facebook/react/blob/aeda7b745d9c080150704feb20ea576238a1b9a1/packages/shared/invariant.js

    你找到的代码实际上是这样的 react.development.js公司 是透明的。如果你不熟悉脱毛,快 read about it here 但它通常是为了使用旧浏览器可能还不支持的语言功能(比如 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;
      }
    }
    

    注意 if (__DEV__) 比特? __DEV__ 是一个通常设置为 true false 取决于它们运行的是什么类型的构建,因此在开发构建中,这段代码将看起来像

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

    现在,当优化该构建时,优化器将检测到 if 检查是完全多余的,因为它总是计算为真,所以它只是删除它。任何人都能猜到为什么它决定保留 { } 但它可能是为了避免意外引入范围错误。

    现在看看 production version 反应构造。它将与 __开发__ 标志设置为 优化器会意识到它可以删除整个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;
        }
    },
    

    这个 if(!a) 检查是否真的 if (!condition) 从原始源代码中检查(变量名已被缩小以保存更多字节)。对…的呼唤 validateFormat() 在源代码中可以看到的已不存在,因为整个函数只是一个no操作,因为它从未在now removed中重新分配 如果(开发) 封锁。

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

    所以,就这样。我希望这能澄清一些事情,我不会让你更困惑。你发现的基本上只是一个在构建步骤中运行的小型化过程的遗留物。