exports和module exports

exports 和 module exports

require 中路径 ./ 和 ../区别

这两种写法都是属于相对路径的使用符号

  • ”./“:代表与当前文件所在的目录。
  • ”../“:代表上一层目录。
  • ”/“:开头代表根目录。

首先我们来建立一些文件路径如下图: image

1. 文件在当前目录:

在page2.html 中访问 page2image.jpg。

page2.html:

 <img src="./page2image.jpg">
 或者
 <img src="page2image.jpg">

2. 文件在上一层目录:

  • page1.html 中访问 image 下的 image.jpg。

page1.html:

<img src = "../image/image.jpg">
  • page2.html 中访问 image 下的 image.jpg。 page2.htl:
<img src = "../../image/image.jpg">

3. 文件在下一层目录:

page1.html 中访问 text2 文件夹下的 page2image.jpg。

page1.html:

<img src = "./text2/page2image.jpg">
或者
<img src = "text2/page2image.jpg">

4. 根目录表示法:

任何页面访问 image 下的 image.jpg。

<img src = "/image/image.jpg">

exports 和 moudle exports 区别:

1. module.exports 初始值为一个空对象 {}

2. exports 是指向的 module.exports 的引用

3. require() 返回的是 module.exports 而不是 exports

每一个node.js执行文件,都自动创建一个module对象,同时,module对象会创建一个叫exports的属性,初始化的值是 {}

module.exports 被改变的时候,exports不会被改变,而模块导出的时候,真正导出的执行是module.exports,而不是exports:

foo.js
exports.a = function(){
  console.log('a')
 }

 module.exports = {a: 2}
 exports.a = 1 
text.js
var x = require('./foo');

 console.log(x.a) // => 2

exports在module.exports 被改变后,失效。

一. require

从require导入方式去理解,关键有两个变量(全局变量module.exports,局部变量exports)、一个返回值(module.exports)

function require(...) {  
  var module = { exports: {} };
  ((module, exports) => {
    // 你的被引入代码 Start
    // var exports = module.exports = {}; (默认都有的)
    function some_func() {};
    exports = some_func;
    // 此时,exports不再挂载到module.exports,
    // export将导出{}默认对象
    module.exports = some_func;
    // 此时,这个模块将导出some_func对象,覆盖exports上的some_func    
     // 你的被引入代码 End
  })(module, module.exports);
  //  不管是exports还是module.exports,最后返回的还是module.exports 
  return module.exports;
}

二. Demo 事例

demo01: 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);

从 demo01 中,可以看出来:

  1. 每个js文件一创建,都有一个var exports = module.exports = {};,使exports和module.exports都指向一个空对象。
  2. module是全局内置对象,exports是被var创建的局部对象。
  3. module.exports和exports所指向的内存地址相同。

Dome02: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  

由 demo02 可以知道: 1. module.exports像是exports的大哥,当module.exports以{}整体导出时会覆盖exports的属性和方法。

  1. 注意,若只是将属性/方法挂载在module.exports./exports.上时,exports.id=1和module.exports.id=100,module.exports.id=function(){}和exports.id=function(){},最后id的值取决于exports.id和module.exports.id的顺序,谁在后,就是最后的值
  2. 若exports和module.exports同时赋值时,exports所使用的属性和方法必须出现在module.exports,若属性没有在module.exports中定义的话,出现undefined,若方法没有在module.exports中定义,会抛出TypeError错误。
// 2.js
module.exports.id = function () {
    console.log('module.exports');
};

exports.id = function () {
    console.log('exports');
};

module.exports.id = 'moudle.exports的id';

exports.id = 'exports的id';


// 1.js
var a = require('./2.js');
console.log(a.id); // exports的id
console.log(a.id()); // exports

Dome3: 4.js、5.js

// 5.js

function View(){
    this.test1 = function () {
        console.log('test1');
    }
}

View.prototype.test = function(){
    console.log('test')
};

View.test1 = function(){
    console.log('test1-out')
};

module.exports = View;
// 4.js

const a = require('./5.js');
// new一个对象
const person = new a();

console.log(person.test1()); // => test1
console.log(person.test()); // => test
console.log(a.test1()); // => test1-out  无法打出 test1 因为没有实例化new a();
console.log(a.test()); // => 报错:没有 new a()实例化无法直接调用原型方法。
console.log('person: '+person); // => [object Object]
console.log('a: '+a); // => 如下:
/**
 * function View(){
 *     this.test1 = function () {
 *  console.log('test1');
 * }
 *}
 */
console.log('a.test: '+a.test); // => undefined
console.log('a.test1: '+a.test1); // => 如下:
/**
 * function (){
 *   console.log('test1-out')
 * }
 */


console.log('a.test1(): '+a.test1()); // => test1-out

或者是 6.js、7.js

// 6.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
// 7.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.属性/方法。要是导出多个属性或方法或使用对象构造方法,结合prototype等,就建议使用module.exports = {}。

但是要注意什么时候该 new 实例。