Learning ReactNative (一) : JavaScript模块基本原理与用法
在使用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打印出来是一个对象。
我们会发现我们常用的console是global对象的其中一个属性。而我们平时在使用的时候不需要写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 from、export以及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模块基本原理与用法的更多相关文章
- Javascript模块规范(CommonJS规范&&AMD规范)
Javascript模块化编程(AMD&CommonJS) 前端模块化开发的价值:https://github.com/seajs/seajs/issues/547 模块的写法 查看 AMD规 ...
- 转: javascript模块加载框架seajs详解
javascript模块加载框架seajs详解 SeaJS是一个遵循commonJS规范的javascript模块加载框架,可以实现javascript的模块化开发和模块化加载(模块可按需加载或全部加 ...
- javascript模块加载框架seajs详解
SeaJS是一个遵循commonJS规范的javascript模块加载框架,可以实现javascript的模块化开发和模块化加载(模块可按需加载或全部加载).SeaJS可以和jQuery完美集成,使用 ...
- 爬虫 requests模块的其他用法 抽屉网线程池回调爬取+保存实例,gihub登陆实例
requests模块的其他用法 #通常我们在发送请求时都需要带上请求头,请求头是将自身伪装成浏览器的关键,常见的有用的请求头如下 Host Referer #大型网站通常都会根据该参数判断请求的来源 ...
- 关于javascript模块加载技术的一些思考
前不久有个网友问我在前端使用requireJs和seajs的问题,我当时问他你们公司以前有没有自己编写的javascript库,或者javascript框架,他的回答是什么都没有,他只是听说像requ ...
- (转)深入理解JavaScript 模块模式
深入理解JavaScript 模块模式 (原文)http://www.cnblogs.com/starweb/archive/2013/02/17/2914023.html 英文:http://www ...
- javascript confirm()函数的用法
javascript confirm()函数的用法 confirm():确认消息对话框.用于允许用户做选择的动作.弹出的对话框中包含一确定按钮和一取消按钮. confirm(str) 参数说明: st ...
- 深入理解JavaScript 模块模式
http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html 模块模式是JavaScript一种常用的编码模式.这是一般的 ...
- javascript中的继承用法
本文实例汇总了javascript关于继承的用法,希望本文所述对大家的javascript程序设计有所帮助.分享给大家供大家参考.具体如下:代码如下: /** * 实现子类继承父类,但不会产生多余的属 ...
随机推荐
- install nfs and share file
介绍一下NFS的安装,以及共享文件 NFS(Net File System),通过使用NFS,可以像使用本地文件一样访问远程文件. 它主要解决了数据共享的问题,可以备份容灾. 安装配置 1.以linu ...
- HDU 5304(Eastest Magical Day Seep Group's Summer-环加外向树生成树计数)[Template:Kirchhoff矩阵]
Eastest Magical Day Seep Group's Summer Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 655 ...
- IOS 开发推荐经常使用lib
1. Mantle Mantle 让我们能简化 Cocoa 和 Cocoa Touch 应用的 model 层.简单点说.程序中常常要进行网络请求,请求到得通常是 json 字符串.我们通常会建一个 ...
- Food hub
Work center List Tillage 耕作 Hand harvest 手工采收 Planting 种植 Cultivating 培养 Mulching 覆盖 Dig harvest 挖地采 ...
- DDR硬件设计要点详解(包括电源部分)
转自 http://www.fairchildic.org/module/forum/thread-658-1-1.html (原帖包括详细的附件内容) 1. 电源 DDR的电源可以分为三类A.主电源 ...
- kubernetes之故障现场二,节点名称冲突
系列目录 问题描述:测试环境由于异常断电导致服务器重启一后,有一个节点的状态一直是NotReady.通过journalctl -f -u kubelet没有错误日志输出.通过tail /var/log ...
- nanonets
https://app.nanonets.com/ List of Models IMAGES Images: Image Categorization Beta Input: Image Out ...
- Install Server Backup Manager on CentOS, RHE, and Fedora
Skip to end of metadata Added by Internal, last edited by Internal on Aug 25, 2014 Go to start of me ...
- 转_【大话IT】你离大数据架构师有多远?
话题背景: 首先,先科普下“数据架构师”的相关职责:数据架构师要负责建立和维持公司数据储存的技术基准,策划硬体和软体的结构,确保数据储存系统可以支持未来的数据量和分析需求. 据了解,美国地区数据架构师 ...
- dsoframer注冊说明及在VC2010使用
一.dsoframer在XP.win7和win8中的注冊方法. 从微软站点下载DsoFrmaer_KB311765_x86.exe,双击解开后得到的dsoframer.ocx等文件. (一)XP注冊 ...