在使用ReactNative进行开发的时候,咱们的工程是模块化进行组织的。在npmjs.com几十万个库中,大部分都是遵循着CommonJS规则的。在ES6中引入了class的概念,今后JavaScript也能够更加方便地进行OOP编程。可是不变的是,即便在使用OOP编程,其依赖组织方式仍然是模块化的。所以,咱们十分有必要了解JavaScript中模块的基本原理,以便在以后的开发过程当中能少犯错误,更好的理解整个工程的结构。javascript
本文以更容易理解的意识流的方式简要的介绍了一下require.js、module、exports以及ES6中的Module中的一些差别以及注意事项。若有不对之处欢迎批评指正,另外欢迎讨论技术问题。php
require语句在平常开发中十分的常见,它常常出如今.js
文件的头部,也能够出如今某段语句中。举个:css
//文件 module.js console.log(module); //文件 index.js require('./module.js');
在命令行下执行 node index.js
,结果输出以下:html
Module {
id: '/Users/xiadongxiang/JS/module/module.js', exports: {}, parent: Module { id: '.', exports: {}, parent: null, filename: '/Users/xiadongxiang/JS/module/index.js', loaded: false, children: [ [Circular] ], paths: [ '/Users/xiadongxiang/JS/module/node_modules', '/Users/xiadongxiang/JS/node_modules', '/Users/xiadongxiang/node_modules', '/Users/node_modules', '/node_modules' ] }, filename: '/Users/xiadongxiang/JS/module/module.js', loaded: false, children: [], paths: [ '/Users/xiadongxiang/JS/module/node_modules', '/Users/xiadongxiang/JS/node_modules', '/Users/xiadongxiang/node_modules', '/Users/node_modules', '/node_modules' ] }
从上面的代码以及输出,咱们能够概括出:java
paths
字段,聪明的你确定猜到了一些东西。具体也能够参考一下这个连接。在module.js
中调用的module
,是从哪里来的? 莫慌,咱们一步一步探究。node
首先,在javascript运行环境中有一个叫作global
的变量(若是在浏览器中叫作window
),有兴趣的同窗能够把global
打印在控制台上看看,global打印出来是一个对象。ios
咱们会发现咱们经常使用的console
是global
对象的其中一个属性。而咱们平时在使用的时候不须要写global.console.log('hello')
,而只须要写console.log('hello')
.对于大多数场景下,咱们只须要知道global有这么一个提供命名空间的做用就好了。es6
熟悉C++的同窗可能比较容易联想到using namespace std;
.typescript
废话了那么多,既然global为咱们提供了命名空间,那么上一节中打印出来的东西是否global中的呢?试一试便知:npm
咱们稍微修改一下上一小节的代码:
//文件 module.js console.log(global.module); //文件 index.js require('./module.js');
运行结果:
undefined
这个结果说明了,module不是global命名空间下的东西,那么它到底来自哪里???
上面那个问题,咱们先放一下,答案将在模块加载机制中揭晓。这小节先深刻探究一下exports的用法。
如下是正常的代码:
//index.js var a = require('./module.js'); console.log('in parent:',a); a.a = 2; console.log(a.a,a.getA()); //module.js var a = 1; function getA() { return a; } exports.a = a; exports.getA = getA; console.log('in module: ',module.exports);
运行node index.js
获得结果:
in module: { a: 1, getA: [Function: getA] } in parent: { a: 1, getA: [Function: getA] } 2 1
获得的结论是:
require()
调用返回的结果是module.exports
,而这个exports
则是一个对象,在模块内部,大多数状况下能够等同于module.exports
.2 1
,推断出:exports.a只是对模块内部的var a;
作了一次浅拷贝。若是a是一个Object的场景,咱们能够用指针去理解,一样也是浅拷贝。有哪些状况下exports不等同于module.exports呢?
直接上代码:
//index.js var a = require('./module.js'); console.log('in parent:',a); //module.js var a = 1; function getA() { return a; } exports.a = a; exports.getA = getA; module.exports = { newA:1, newB:2 } console.log('in module module.exports: ',module.exports); console.log('in module exports: ',exports);
运行node index.js
,结果以下:
in module module.exports: { newA: 1, newB: 2 } in module exports: { a: 1, getA: [Function: getA] } in parent: { newA: 1, newB: 2 }
其实这个现象很好理解,咱们能够想象在你的代码运行以前,系统作了这些事情:**exports只是module.exports的浅拷贝**.
var module = { exports:{}, ... }; var exports = module.exports; //接下来运行你的代码
固然,你如果想不开,也能够给module = {};
从新赋值试试... :)
通常状况下二者的使用场景,没有绝对的对与错:
继续举个方便理解:**module.exports***
//index.js var A = require('./module.js'); var a = new A(); //module.js class a { } module.exports = a;
exports.*能够利用到对象解构赋值:
//index.js var {A,B} = require('./module.js'); console.log(A,B()); // 1 1 //module.js var a = 1; exports.A = a; exports.B = function(){ return a; }
咱们知道,javaScript
是脚本语言,执行方式是解释执行。注定了JSContext
是很是灵活的,咱们不能用强类型语言的标准去理解。
模块加载原理仍是比较复杂的,用最简单的方式去解释,当一个文件被require
的时候,其实咱们能够理解成:
(function (exports, require, module, __filename, __dirname) { // 模块源码 --begin // 模块源码 --end return module.exports; });
具体一点:
在ES6中引入了 import from
、export
以及export default
等更加先进的语法。
可是万变不离其宗,语法和更详细的介绍能够参考阮一峰老师的ECMAScript 6 入门.
这里只提2点:
举个:
#include <iostream> using namespace std; int main() { int a = 1; int b = a; int &c = a; b = 2; c = 3; cout << a <<endl; int *_a = new int(1); int *_b = _a; int *&_c = _a; cout<< *_a <<','<<*_b<<','<<*_c<<endl; delete _a; _b = new int(2); _c = new int(3); cout<< *_a <<','<<*_b<<','<<*_c<<endl; delete _a; delete _b; return 0; }
输出:
3 1,1,1 3,2,3
其中b的作法是commonJS,c的作法是es6.