在使用ReactNative进行开发的时候,我们的工程是模块化进行组织的。在npmjs.com几十万个库中,大部分都是遵循着CommonJS规则的。在ES6中引入了class的概念,从此JavaScript也可以更加方便地进行OOP编程。但是不变的是,即使在使用OOP编程,其依赖组织方式仍然是模块化的。因此,我们十分有必要了解JavaScript中模块的基本原理,以便在之后的开发过程中能少犯错误,更好的理解整个工程的结构。

本文以更容易理解的意识流的方式简要的介绍了一下require.js、module、exports以及ES6中的Module中的一些差异以及注意事项。如有不对之处欢迎批评指正,另外欢迎讨论技术问题。

本文结构:

  • 从require语句说起
  • module是什么鬼?
  • 来点正常点的写法:exports的用法
  • module.exports与exports傻傻分不清楚
  • 简单概括模块加载机制
  • ES6中的Module以及差异
  • 虾米ReactNative模块规范

从 require语句说起


require语句在日常开发中十分的常见,它经常出现在.js文件的头部,也可以出现在某段语句中。举个:

//文件 module.js
console.log(module); //文件 index.js
require('./module.js');

在命令行下执行 node index.js,结果输出如下:

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' ] }

从上面的代码以及输出,我们可以归纳出:

  • 调用require的时候,require所指向的代码会被解释执行一遍。
  • 从输出上看,这是对于一个模块的描述对象,其中id使用了文件的绝对路径,我们可以推断出在RN、Node开发中:文件即模块。
  • 此外,关于require之后的文件路径的写法非常简单,看一下上面的输出中的paths字段,聪明的你肯定猜到了一些东西。具体也可以参考一下这个链接

module是什么鬼?

module.js中调用的module,是从哪里来的? 莫慌,我们一步一步探究。

首先,在javascript运行环境中有一个叫做global的变量(如果在浏览器中叫做window),有兴趣的同学可以把global打印在控制台上看看,global打印出来是一个对象。

我们会发现我们常用的consoleglobal对象的其中一个属性。而我们平时在使用的时候不需要写global.console.log('hello'),而只需要写console.log('hello').对于大多数场景下,我们只需要知道global有这么一个提供命名空间的作用就行了。

熟悉C++的同学可能比较容易联想到using namespace std;.

废话了那么多,既然global为我们提供了命名空间,那么上一节中打印出来的东西是否global中的呢?试一试便知:

我们稍微修改一下上一小节的代码:

//文件 module.js
console.log(global.module); //文件 index.js
require('./module.js');

运行结果:

undefined

这个结果说明了,module不是global命名空间下的东西,那么它到底来自哪里???

来点正常点的写法:exports的用法


上面那个问题,我们先放一下,答案将在模块加载机制中揭晓。这小节先深入探究一下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的场景,我们可以用指针去理解,同样也是浅拷贝。

module.exports与exports傻傻分不清楚


有哪些情况下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 一般用于只输出一个东西。多为class,function,Object;
  • exports. 一般用于输出多种东西;

继续举个方便理解:**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;
});

具体一点:

  • 计算绝对路径
  • 如果有缓存,取出缓存
  • 是否为内置模块
  • 生成模块实例,存入缓存
  • 加载模块
  • 输出模块的exports属性

ES6中的Module以及差异


在ES6中引入了 import fromexport以及export default等更加先进的语法。

但是万变不离其宗,语法和更详细的介绍可以参考阮一峰老师的ECMAScript 6 入门.

这里只提2点:

  • ES6是动态加载(类似Lazy Load)的,在某些程度上启动起来更快.
  • ES6中export出去的是引用!!是引用!!是引用!!

举个:

#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.

Learning ReactNative (一) : JavaScript模块基本原理与用法的更多相关文章

  1. Javascript模块规范(CommonJS规范&&AMD规范)

    Javascript模块化编程(AMD&CommonJS) 前端模块化开发的价值:https://github.com/seajs/seajs/issues/547 模块的写法 查看 AMD规 ...

  2. 转: javascript模块加载框架seajs详解

    javascript模块加载框架seajs详解 SeaJS是一个遵循commonJS规范的javascript模块加载框架,可以实现javascript的模块化开发和模块化加载(模块可按需加载或全部加 ...

  3. javascript模块加载框架seajs详解

    SeaJS是一个遵循commonJS规范的javascript模块加载框架,可以实现javascript的模块化开发和模块化加载(模块可按需加载或全部加载).SeaJS可以和jQuery完美集成,使用 ...

  4. 爬虫 requests模块的其他用法 抽屉网线程池回调爬取+保存实例,gihub登陆实例

    requests模块的其他用法 #通常我们在发送请求时都需要带上请求头,请求头是将自身伪装成浏览器的关键,常见的有用的请求头如下 Host Referer #大型网站通常都会根据该参数判断请求的来源 ...

  5. 关于javascript模块加载技术的一些思考

    前不久有个网友问我在前端使用requireJs和seajs的问题,我当时问他你们公司以前有没有自己编写的javascript库,或者javascript框架,他的回答是什么都没有,他只是听说像requ ...

  6. (转)深入理解JavaScript 模块模式

    深入理解JavaScript 模块模式 (原文)http://www.cnblogs.com/starweb/archive/2013/02/17/2914023.html 英文:http://www ...

  7. javascript confirm()函数的用法

    javascript confirm()函数的用法 confirm():确认消息对话框.用于允许用户做选择的动作.弹出的对话框中包含一确定按钮和一取消按钮. confirm(str) 参数说明: st ...

  8. 深入理解JavaScript 模块模式

    http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html 模块模式是JavaScript一种常用的编码模式.这是一般的 ...

  9. javascript中的继承用法

    本文实例汇总了javascript关于继承的用法,希望本文所述对大家的javascript程序设计有所帮助.分享给大家供大家参考.具体如下:代码如下: /** * 实现子类继承父类,但不会产生多余的属 ...

随机推荐

  1. hdoj 4828 卡特兰数取模

    Grids Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Total Sub ...

  2. XPath可以快速定位到Xml中的节点或者属性。XPath语法很简单,但是强大够用,它也是使用xslt的基础知识。

    示例Xml: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?xml versio ...

  3. iOS7系统中的坑——UITableViewCellScrollView

    今天开完一系列例会后,终于迎来放假的时候了,本来中午就要下班走的,想着火车票现在还很早,也就不急着走,闲着无聊,想着之前要写的内容,索性写一篇聊以打发时光,也希望对其他人有所帮助吧. 现在iOS的最新 ...

  4. 转帖:HttpStatusCode状态说明C#版

    Continue 等效于 HTTP 状态 100.Continue 指示客户端可能继续其请求. SwitchingProtocols 等效于 HTTP 状态 101.SwitchingProtocol ...

  5. Go Programming Blueprints 读书笔记(谈到了nsq/mgo处理数据持久化,可是业务逻辑不够复杂)

    Go Programming Blueprints http.Handle("/", &templateHandler{filename: "chat.html& ...

  6. pascals-triangleI、II——生成规律的三角形

    1.Given numRows, generate the first numRows of Pascal's triangle. For example, given numRows = 5,Ret ...

  7. flex 节点删除

    <mx:Script>        <![CDATA[            protected function btn1_clickHandler(evt:MouseEvent ...

  8. mysql 分表的3种方法

    http://blog.51yip.com/mysql/949.html       CSDN - Mysql MERGE分表对大数据量的处理     实战经验: 要分表的表引擎必须是myisam类型 ...

  9. 简单理解javascript中的原型对象,实现对之间共享属性和行为

    javascript中提供了构造函数.可以方便的创建对象. 典型的构造函数例如以下: function Person(name, age) { this.name = name; this.age = ...

  10. 转:DDR原理详解

    首先,我们先了解一下内存的大体结构工作流程,这样会比较容量理解这些参数在其中所起到的作用.这部分的讲述运用DDR3的简化时序图. DDR3的内部是一个存储阵列,将数据“填”进去,你可以它想象成一张表格 ...