2.模块化Module

模块化Module

Nodejs采用模块化方式管理和组织代码,Nodejs的所有功能都存在每个模块中

1.模块的了解

1.1什么是模块

模块:一个具有特定功能的文件就是模块
模块的优点:有了模块,我们就可以风场方式使用这些模块,因为模块总是完成特定的功能,如果要修改模块中的某个功能,那么需要这个模块文件即可,模块独立于每一个文件中,不影响模块的代码

模块之间相互独立,如果一个模块引入另一个模块要使用里面的值,我们就需要在被引入的模块中暴露这些值

1.2什么是模块化

模块化:讲一个复杂的程序一局一定的规则(规范)封装成几个模块(文件),并进行组合在一起,每个模块内部数据实现私有的,知识向外部暴露一些接口(方法)与外部其他模块通信

模块化的进化

//全局开发模式
//最早期所有的js代码写在一个js文件中
function foo(){}
function bar(){}
//造成的问题还就是代码量过大以后,
//Global全局被污染,很容易导致命名冲突

后来对代码进行简单封装

// 简单封装:Namespace 模式 命名空间
var Namespace = {
      foo: function(){},
      bar: function(){},

}
//减少Global上的变量数目
//本质就是对象 ,不太安全

因为简单封装的不安全,又出现了IIFE模式

//匿名闭包
 var Module = (function(){
        var foo = function(){

          }
          return {
                 foo:foo
          }

})()
Module.foo()
//函数是JavaScript 的Local Scope (局部作用域)

有的时候一个模块还可能需要依赖其他的模块,我们就需要在升级一下,将以来的模块注入

//增强封装
var Module = (function($){
  var $body = $(body);
  var foo = function(){

    }
    return {
              foo:foo
    }
})($)
//这句就是模块模式 ,也就是模块实现的基石
1.3为什么要模块化

1.降低复杂度
2.提高解耦性
3.部署方便

1.4模块化的好处

1.避免命名冲突(减少命名空间的污染)
2.更好的分类,按需加载
3.更好的复用性
4.高可维护性

同时模块化页导致了很大的问题

1.请求过多
2.依赖模糊

<script  src="a.js" ></script>
<script  src="b.js" ></script>
<script  src="c.js" ></script>

因为造成这些问题所以需要模块化规范

1.5模块的规范

1.CommonJS(NodeJS)
2.AMD
3CMA
4.ESModule(ES模块化)

1..5.1模块的规范

CommonJS是诞生比较早的,NodeJS就是采用了CommonJS的规范定义模块的,但是ComminJS采用的是同步架子啊文件的方式,只适合用于服务端

CommonJS API:不是一个语言,也不是一个平台,他知识一个标准,主要是对原有的JavaScript标准API进行了增强

CommonJS是一种规范,NodeJS是对这个对方的实现

在规范中每一个文件都是一个独立的模块
特点:
1.在服务器端:模块的加载是运行时同步加载
2.在浏览器端,模块需要体检编译打包处理

2.NodeJS模块化

2.1模块分类
2.1.1 模块分三种

1.系统模块
NodeJS开发团队已经开发了很多功能模块,不需要卸载,直接引入就可以使用
2.第三方模块
第三方模块必须先安装在使用
使用包管理工具npm进行安装
第三方模块早npmjs.org中
3.自定义模块
自己写一个js文件就是模块

2.1.2 也有将模块分两种的

1.核心模块(系统模块)
2.文件模块(包括第三方模块和自定义模块)

2.1.3 如果功能模块按照关系分

1.主模块
主模块就是被Node执行的模块,通常喜欢命名app.js, main.js,index.js
一个项目只能有一个主模块(也叫入口文件,就是整个项目的启动模块)

2.依赖模块

2.2 内置模块

NodeJS中的内置了很多模块,可以直接使用
require用来进行引用,国际惯例,你接受的名字最好和模块的名字一样

const http = require('http')

如果特别长可以简写

const qs = require('querystring')

内置模块的引用是无条件的,无路径的

2.3 自定义模块

自定义模块就是自己写的一个模块文件
每一个js都是一个模块,Node.js使用commonjs规范
定义a模块

//a.js
var str = '这个是一个字符串'
exports.str = str; //exports是到出(也可以叫暴露)对象

定义b模块导入a模块

//b.js
var aa = require('./a.js'');
//此时aa是a模块中的暴露对象,如果要使用暴露的字符串str
console.log(aa.str)

然后通过node.js执行b.js

node b.js

注意点:
1.会发现require()谁就会执行谁,会让a.js执行
2.require()引入的模块如果有异步语句,不会等死,会奖所有的同步执行完毕后执行异步语句
3.如果多层引用会把引入文件里的引入执行干净了
4.如果循环引用,A引用B, B引入A ,Node会很智能的组织好第二次引入

3.Common模块化

3.1 模块的组成

在模块中打印当前模块的组成信息

console.log(arguments)
console.log(arguments.callee.toString);//打印函数体

打印的arguments对象

[Arguments] {
    '0': {},
    '1':{ 
        [Function: require]
        resolve: { [Function: resolve] paths: [Function: paths] },
        main:
          Module {
              id: '.',
              exports: {},
              parent: null,
              filename: 'C:\\Users\\HiWin10\\Desktop\\study\\app.js',
              loaded: false,
              children: [Array],
              paths: [Array] },
              extensions:[Object: null prototype] { 
                  '.js': [Function], 
                  '.json': [Function], 
                  '.node': [Function] 
              },
              cache:[Object: null prototype] {
                 'C:\\Users\\HiWin10\\Desktop\\study\\app.js': [Module],
                 'C:\\Users\\HiWin10\\Desktop\\study\\wu.js': [Module] 
              } 
         },
    '2':
        Module {
            id: '.',
            exports: {},
            parent: null,
            filename: 'C:\\Users\\HiWin10\\Desktop\\study\\app.js',
            loaded: false,
            children: [ [Module] ],
            paths:[
                'C:\\Users\\HiWin10\\Desktop\\study\\node_modules',
                'C:\\Users\\HiWin10\\Desktop\\node_modules',
                'C:\\Users\\HiWin10\\node_modules',
                'C:\\Users\\node_modules',
                'C:\\node_modules' 
            ] 
        },
                                                          
      '3': 'C:\\Users\\HiWin10\\Desktop\\study\\app.js',
      '4': 'C:\\Users\\HiWin10\\Desktop\\study' 
}

打印函数体

function (exports, require, module,__filename,__dirname){
  console.log(arguments.callee.toString())
}

所有的用户编写的代码都自动封装在一个函数中,函数有五个参数,我们就可以在函数nebula使用五个实参

五个实参

  1. exports 暴露对象,可以将模块中的数据暴露给引入的地方
  2. require 引入模块的函数,用于在一个模块中引入另一个模块,并且将子模块暴露的数据赋值给变量

3.module 模块对象包含了模块的所有的信息(当前模块信息)
4.__filename 当前模块的文件名
5.__dirname 当前模块所在的路径

3.2 require函数(重点)

require(moduleID)函数英语在当前模块中加载和使用别的模块,传入一个模块名,返回一个模块到处对象

'1':{ 
        [Function: require]
        resolve: { [Function: resolve] paths: [Function: paths] },
        main:
          Module {
              id: '.',
              exports: {},
              parent: null,
              filename: 'C:\\Users\\HiWin10\\Desktop\\study\\app.js',
              loaded: false,
              children: [Array],
              paths: [Array] },
              extensions:[Object: null prototype] { 
                  '.js': [Function], 
                  '.json': [Function], 
                  '.node': [Function] 
              },
              cache:[Object: null prototype] {
                 'C:\\Users\\HiWin10\\Desktop\\study\\app.js': [Module],
                 'C:\\Users\\HiWin10\\Desktop\\study\\wu.js': [Module] 
              } 
         },
}

./表示当前目录,模块文件路径的js后缀可以省略

var foo = require('.foo');//当前目录下的foo.js文件
var foo1 = require('./foo.js')

导入如果不加后缀名,会先找js文件,如果没有js文件会找json文件,如果没有会找node文件

注意:
1.引入模块文件有语法错误是会报错
2.引入模块不存在时会报错
3.重复引入模块只会执行一次(返回值会被缓存起来)
4.所有的模块如果没有返回值,导入的模块中将返回空对象
5.刀如自定义模块必须加'./',因为node.js中查找 模块默认是在node_modules目录中查找
6.如果引入第三方模块直接写模块名字就可以了

3.2.1 主模块main

require有一个属性main来确定当前模块是不是主模块,应该应用程序中,通过node启动的模块就是应用模块,页叫主模块

consloe.log(module == require.main)
//ture表示当前模块是主模块,false表示当前模块不是主模块
3.2.2 require.resole()方法获取绝对路径

require.resolve()方法 传入一个相对路径参数,返回凭借后的绝对路径

console.log(require.resolve("./module"))
3.2.3 require.cache属性

require.cache属性值是一个对象,缓存了所有已经加载的模块

console.log(require.cache)
3.3 exports导出对象

exports对象是当前模块的导出对象,用于导出模块共有的方法和属性,别的模块在通过require函数导入使用当前模块时就会获得当前模块的exports对象

ps:例子

exports.hello = function(){
      console.log("hello world")
}

注意事项:
1.exports是module.exports对象的引用
2.exports不能改指向,只能添加属性和方法

如果希望更改暴露指向,那么我们就需要使用module.exports进行暴露

var aa  = function(){
     console.log(11)
}
module.exports = aa;
3.4 module模块对象(重中之重)
'2':
    Module {
        id: '.',    // 模块的id ,模块的名称,  .代表主模块(其他模块,id就是模块的文件路径)
        exports: {},    // 当前模块导出的内容(重点,其他了解)
        parent: null,   // 引入当前模块的父模块,
        path: '' ,   // 当前文件夹的例子
        filename: 'C:\\Users\\HiWin10\\Desktop\\study\\app.js', //当前模块文件路径
        loaded: false, 
        children: [ [Module] ],   // 子模块
        paths:[      // 默认模块查找路径,当前目录找不到,就当上一层文件夹找
            'C:\\Users\\HiWin10\\Desktop\\study\\node_modules',
            'C:\\Users\\HiWin10\\Desktop\\node_modules',
            'C:\\Users\\HiWin10\\node_modules',
            'C:\\Users\\node_modules',
            'C:\\node_modules' 
        ] 
    },

module对象可以访问当前模块的一些相关信息,但最多的用途是替换当前模块到处的对象

module.exports = {}

例如:默认的到处对象是一个对象,我们可以改变,让导出的对象为普通的函数或其他数据类型的值

module.exports = function(){
  console.log('hello world')
}

module.exports 真正的暴露对象,exports知识对module.exports的引用

3.5 模块初始化

每一个模块中的js代码尽在模块第一次使用时执行一次,并在执行过程中初始化模块的导出对象,之后会将导出对象,之后会将导出的对象缓存起来,被重复利用

4.NodeJS作用域

4.1 作用域

作用域:规定一个变量和函数可以使用的范围
作用域分为两种:全局作用域和局部作用域

在Node中每一个js文件都是单独的作用域,因为node的模块化会将每一个文件中的代码封装在一个函数内部,这和DOM浏览器开发不同,浏览器中,var a ,此时的a会自动成为window的属性,此时js文件和js文件共享作用域,大师Node.js中,每个js文件是独立作用域不共享,所以Node中每一个文件中的变量都是私有的

例子:

var num = 10

NodeJS会在执行之前编译这个模块为

function(exports,require,module,__filename,__dirname){
    var  num = 10
}

4.2暴露(导出)

所以如果让其他作用域使用本作用域中的值呢,我们采用暴露的方式将值抛出去,也就是暴露共享数据

暴露数据

module.exports = { num: 10}

使用exports暴露

//test.js
var a = 100;
exports.a = a;
//exports是一个对象,给这个对象添加了一个属性a,a的值就是本作用域的变量值

此时当我们通过require引入test.js的时候,就可以拿到这个值

//b.js
var test = require('./text.js');
//此时test是引入对象,就是exports对象,如果要获取a的值应该用
//test.a
console.log(test.a)

注意,模块叫test问文件,定义接收的变量名也会叫test,其他名字不会报错,但我们不会这么做

是用module.exports 暴露数据

刚才我么使用exports向外暴露一些值,但是我们很不多方便,这个东西必须是exports的属性,require()导入的时候,返回的是一个对象我们还得去对象里那属性和方法,如果仅仅只能暴露一个东西,我戏外向外暴露一个类.此时就很不方便

//导出类 Person.js
function Person(name,age,sex){
  this.name = name;
  this.age = age;
  this.sex = sex;
}
exports.Person = Person;

导入

var Person = require('./Person.js');
//此时的Person是exports对象,如果想使用这个类得Person.Person()使用
var xiaoming = new Person.Person('小明',12,'男')

此时就会发现使用很不方便

如果js里仅仅只暴露一个东西,我们可以使用module.exports来暴露
这样module.exports暴露的是什么,那么require()导入返回的还就是什么

module.exports暴露一个东西,exports暴露多个东西

//导出类 Person.js
function Person(name,age,sex){
  this.name = name;
  this.age = age;
  this.sex = sex;
}
exports.Person = Person;

导入

var Person = require('./Person.js');
//如果采用的module.exports暴露的,这里Person就是暴露的哪个类,所以我们可以直接使用
var xiaoming = new Person('小明',12,'男')

这样使用的时候就方便很多

4.3定义全局变量
var username = 'xiaoming'
global.name = username

使用时可以不写global

console.log(name)

5.模块的共性

所用的慢慢看化都有一个共性,模块的功能都是把代码放到一个独立的函数中
1.模块中使用的var定义的变量都是局部变量
2.模块中定义的函数也是局部的
3.模块有一个模块对象,包含moduleName(模块名),exports(导出对象)
4.如果模块中需要,暴露方法或者属性给外部使用,那么就是像exports对象上添加
5.导入一个模块使用require('moduleName'),该方法返回的是模块对象的expots

var aa = require('./a.js')

6.关于路径与后缀名情况

/表示绝对路径
./表示相对路径

如果在引入模块时不传入后缀名,会依次赵后缀名为.js,.json,node,都找不到就报错

若果不写路径则认为是模块内置模块或各级node_module文件夹中的第三方模块

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,185评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,652评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,524评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,339评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,387评论 6 391
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,287评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,130评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,985评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,420评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,617评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,779评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,477评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,088评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,716评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,857评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,876评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,700评论 2 354