npm require 浅拷贝
发布于 7 年前 作者 cuijing 8615 次浏览 来自 问答
  • 当前 Bug 的表现(可附上截图)
  • 预期表现
  • 复现路径
  • 提供一个最简复现 Demo

小程序会在每个npm包的入口文件前加入一段模块打包代码,类似webpack的,下面是其中 __REQUIRE__ 函数的实现

var __REQUIRE__ = function (modId, source) {
  if (!__MODS__[modId])
    return require(source);
 
  if (!__MODS__[modId].status) {
    var m = { exports: {} };
    __MODS__[modId].status = 1;
    __MODS__[modId].func(__MODS__[modId].req, m, m.exports);
 
    if (typeof m.exports === "object") {
      Object.keys(m.exports).forEach(function (k) {
        __MODS__[modId].m.exports[k] = m.exports[k];
      });
 
      if (m.exports.__esModule)
        Object.defineProperty(__MODS__[modId].m.exports, "__esModule", { value: true });
 
    } else {
      __MODS__[modId].m.exports = m.exports;
    }
  }
 
  return __MODS__[modId].m.exports;
};

可以看到,这里判断了如果一个模块导出的是一个对象,就会对其做一层的拷贝,这不是浅拷贝,浅拷贝是直接赋值。这一层的拷贝会导致引用出问题

以下是webpack的require实现

function __webpack_require__(moduleId) {
  /******/
  /******/                // Check if module is in cache
  /******/                if (installedModules[moduleId]) {
  /******/                        return installedModules[moduleId].exports;
    /******/
  }
  /******/                // Create a new module (and put it into the cache)
  /******/                var module = installedModules[moduleId] = {
  /******/                        i: moduleId,
  /******/                        l: false,
  /******/                        exports: {},
  /******/                        hot: hotCreateModule(moduleId),
  /******/                        parents: (hotCurrentParentsTemp = hotCurrentParents, hotCurrentParents = [], hotCurrentParentsTemp),
  /******/                        children: []
    /******/
  };
  /******/
  /******/                // Execute the module function
  /******/                modules[moduleId].call(module.exports, module, module.exports, hotCreateRequire(moduleId));
  /******/
  /******/                // Flag the module as loaded
  /******/                module.l = true;
  /******/
  /******/                // Return the exports of the module
  /******/                return module.exports;
  /******/
}

可见并没有做一层的拷贝。

1 回复

这个拷贝当初是为了兼容有弱循环依赖的包而加的,可以试试工具的 nightly 版本,加了 getter 和 setter,应该仍然有拷贝,不过对于运行时修改 module.exports 应该也会同步过去。

另外这里仍有一个已知问题,就是原型链没有被拷贝过去,这个修复近期也会跟 nightly 版本出去。

回到顶部