游乐游手机版
首页/前端开发/文章详情

深入分析Sea.js中module.exports和exports的区别与联系及常见错误总结

时间:2026-06-10 06:57
Sea js和Node js遵循CommonJS规范,module exports是模块最终返回的对象,exports是其引用快捷方式。为exports重新赋值会断开与module exports的连接;整体赋值module exports会覆盖exports上的属性。简单导出用exports,复杂场景用module exports。

一、官方文档解读

在探讨具体代码之前,我们先来了解Node官方文档对 exports 和 module.exports 的说明。由于SeaJs与Node.js均基于CommonJS规范,查阅官方文档是最可靠的途径。

Module.exports

The module.exports object is created by the Module system. Sometimes this is not acceptable; many want their module to be an instance of some class. To do this, assign the desired export object to module.exports. Note that assigning the desired object to exports will simply rebind the local exports variable, which is probably not what you want to do.

官方文档指出:module.exports对象由模块系统自动创建。当你期望模块表现为某个类的实例时,需要将待导出的对象赋值给module.exports。需特别留意:若直接将对象赋值给exports,仅仅是对局部变量exports的重新绑定,通常不是期望的行为。

exports

The exports variable is a vailable within a module's file-level scope, and is assigned the value of module.exports before the module is evaluated. It allows a shortcut, so that module.exports.f = ... can be written more succinctly as exports.f = .... However, be aware that like any variable, if a new value is assigned to exports, it is no longer bound to module.exports:

再看exports:exports变量在模块的文件级作用域中有效,在模块执行前已被赋值为module.exports。它提供了一种快捷方式:module.exports.f = ...可以简写为exports.f = ...。但请注意,如果给exports赋予了新值,它将不再绑定到module.exports——即断开了引用关系。

require

从require的导入逻辑来看,关键涉及两个变量(全局的module.exports和局部的exports),最终返回值始终是module.exports。下面这段简化的伪代码可以帮助你理解其机制:

function require(...) {
  var module = { exports: {} };
  ((module, exports) => {
    // 你的被引入代码
    // 默认都有 var exports = module.exports = {};
    function some_func() {};
    exports = some_func;
    // 此时exports不再挂载到module.exports,导出的是默认空对象{}
    module.exports = some_func;
    // 此时整个模块导出some_func对象,覆盖了exports上的东西
  })(module, module.exports);
  // 不管exports怎么折腾,最后返回的都是module.exports
  return module.exports;
}

二、Demo示例

理论再多也不如直接运行代码。下面几个例子将帮助你彻底搞懂这两者的区别。

示例一:1.js

console.log(exports); // {}
console.log(module.exports); // {}
console.log(exports === module.exports);  // true
console.log(exports == module.exports);   // true
/**
 Module {
   id: '.',
   exports: {},
   parent: null,
   filename: '/1.js',
   loaded: false,
   children: [],
   paths: [ '/node_modules' ]
 }
 */
console.log(module);

通过本例可以验证以下几点:

  • 每个js文件创建时,内部已经存在 var exports = module.exports = {};,因此exportsmodule.exports最初指向同一个空对象。
  • module是全局内置对象,而exports是由var声明的局部变量。
  • 两者初始时指向同一块内存地址。

示例二:2.js、3.js

// 2.js
exports.id = 'exports的id';
exports.id2 = 'exports的id2';
exports.func = function(){
  console.log('exports的函数');
};
exports.func2 = function() {
  console.log('exports的函数2');
};
module.exports = {
  id: 'module.exports的id',
  func:function(){
    console.log('module.exports的函数');
  }
};

// 3.js
var a = require('./2.js');
// 当属性和函数在module.exports都有定义时:
console.log(a.id); // module.exports的id
console.log(a.func()); // module.exports的函数

// 当属性在module.exports没有定义,函数在module.exports有定义
console.log(a.id2); // undefined
console.log(a.func()); // module.exports的函数

// 当函数在module.exports没有定义,属性在module.exports有定义
console.log(a.id);   // module.exports的id
console.log(a.func2());  // 报错 TypeError: a.func2 is not a function

通过本示例可以明确以下几点:

  • module.exports就像exports的“主导者”。当module.exports{}整体导出时,它会完全覆盖exports上挂载的属性和方法。
  • 如果只是分别给module.exportsexports添加属性/方法(例如 exports.id=1module.exports.id=100),最终结果取决于执行顺序——谁在后面,谁的值生效。

\

  • exportsmodule.exports同时存在时,exports上定义的属性/方法仅在module.exports中没有同名项时才能被访问到。若属性名在module.exports中缺失,获取结果为undefined;若方法名缺失,则会抛出TypeError

示例三:4.js、5.js
展示了module.exports配合对象、原型和构造函数的典型用法。

// 4.js
var a = require('./5.js');
// 若传的是类,new一个对象
var person = new a('Kylin',20);
console.log(person.speak()); // my name is Kylin ,my age is 20

// 若不需要在构造函数时初始化参数,直接调用方法/属性
// a.speak(); // my name is kylin ,my age is 20

// 5.js
// Person类
function Person(name,age){
  this.name = name;
  this.age = age;
}
// 为类添加方法
Person.prototype.speak = function(){
  console.log('my name is '+this.name+' ,my age is '+this.age);
};

// 返回类
module.exports = Person;

// 若构造函数没有传入参数(name,age),直接传入对象
// module.exports = new Person('kylin',20);

绕了这么一大圈,最后给出简单的实践建议:

  • 如果只需要导出一个单一属性或方法,直接使用 exports. 挂载即可。
  • 如果需要导出多个属性/方法,或涉及对象构造方法、结合原型链等复杂场景,建议直接使用 module.exports = {} 整体导出。

文中部分描述可能不够精确,所涉及的要点也可能不全面。若有任何不准确之处,欢迎指正与交流。

总结

以上就是整篇文章的核心内容,希望能帮你彻底理解 exports 和 module.exports 的本质差异。实际编码时多动手测试几个例子,远比死记硬背更有效。

来源:https://www.jb51.net/article/105303.htm
上一篇Sea.js模块加载器常用API快速实用简易文档速查手册 下一篇SeaJS模块依赖加载处理实例分析
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
HTML文档版权声明结构与基础脚手架代码质量解析
前端开发 · 2026-07-07

HTML文档版权声明结构与基础脚手架代码质量解析

版权声明需用语义化``标签,©符号使用`©`实体;年份通过服务端、构建或JS分层兜底;`rel= "license "`仅指向真实许可证文件才有效。

基于HTML模板的组件化全栈交互方案
前端开发 · 2026-07-07

基于HTML模板的组件化全栈交互方案

纯HTML模板仅作静态结构,全栈交互需后端注入数据、前端JS激活模板及用户事件触发双向链路。``标签惰性,不执行脚本或加载资源,需通过克隆填充数据并手动绑定事件实现交互,父子传值依赖JS对象与自定义事件。

Bootstrap修改Badge徽章边框为圆点样式的方法
前端开发 · 2026-07-07

Bootstrap修改Badge徽章边框为圆点样式的方法

将BootstrapBadge改为圆点:自定义类设置等宽高、border-radius:50%、padding:0、font-size:0;Bootstrap5 2+需加display:inline-block;注意垂直对齐,避免min-width干扰,背景色用background-color更可控。

Bootstrap响应式多栏Footer社交图标排版实现
前端开发 · 2026-07-07

Bootstrap响应式多栏Footer社交图标排版实现

使用Bootstrap5的grid系统实现响应式多栏Footer,按断点设置列宽;以FontAwesome处理社交图标,避免使用img;利用list-unstyled组织链接列表;借助flex-column容器配合mt-auto使版权行自动贴底,避免fixed-bottom固定定位的弊端。

如何用Shadow DOM隔离组件内外事件分发
前端开发 · 2026-07-07

如何用Shadow DOM隔离组件内外事件分发

ShadowDOM内部事件默认不穿透影子边界,需显式设置composed:true才能被外部监听。此时event target指向宿主元素,真实触发源需通过event composedPath()[0]获取。原生事件手动派发时也必须声明composed:true。应避免在宿主上依赖event target做逻辑分支,建议在shadowroot内绑定事件或使用