NodeJS_03

 

NodeJS七天课程学习笔记_第3天

课程内容概要:

模块系统
1.核心模块
2.第3方模块
3.自定义模块
issue:循环加载问题

npm以及package.json

express web 框架

增删改查CRUD,MongoDB数据库,文件型数据库(锻炼异步编码)

推荐了书籍<JavaScript高级编程>第3版
<JavaScript语言精粹>

网站运营SEO

自定义Typora样式主题
偏好设置->打开主题文件夹->复制一份,命名:xxx.user.css
例如:设置自动加序号的属性counter-reset:h1;或者counter-increment:h1;

REPL
即Read,Eval,Print,Loop

NodeJS七天课程主讲老师:李鹏周

软件版本
vA.B.C
a:代表去除了一些功能或新增了许多功能
b:代表新增了功能
c:代表修复了bug或性能优化

IE 11 的控制台里可自由选择不同的IE版本号进行兼容性测试

ES6中forEach遍历,由于是新特性,故不兼容低版本如IE8
;[‘面码’,’mathilda’].forEach(function (item,index){
console.log(index + ‘ : ‘ + item)
})

JQuery的低版本的each方法:
1.可以用来遍历jQuery元素
2.在无法使用原生的forEach新特性时,jQuery低版本的each方法,可以做到与IE低版本进行兼容,但是高版本的JQuery
$.each([‘面码’,’mathilda’],function (index,item){
console.log(index + ‘ : ‘ + item)
})

使用forEach来遍历jQuery中的伪数组???
只需先将伪数组转成数组即可
;[].slice.call($(“div”).forEach(function(item,index){
console.log(index + ‘ : ‘ + item)

})

;[].slice.call($(“div”).forEach(function(item,index){
console.log(index + ‘ : ‘ + item)
})

下面是详细说明:
;防止的是不写分号时的3种特殊情况之一()  []  `打头

call($(‘div’))方法,改变了this的值,变成了jQuery的伪数组,
jQuery伪数组的代码类似于:
divArr = {
0 : ‘div_1’,
1 : ‘div_2’,
2 : ‘div_3’,
length: 3
}

[].slice方法不加参数时,返回的是原来长度的数组,
这样一来,返回结果就是一个真正的数组了
[‘div_1′,’div_2′,’div_3’]
这时,就可以使用forEach(function(item,index){})方法了

附上slice方法的内部代码示例:

Array.prototype.mySlice = function(){
	var start = 0
	// jQuery伪数组(即对象)中刚好有一个length属性
	var end = this.length
	if(arguments.length === 1){
		start = arguments[0]
	}else if(arguments.length === 2){
		start = arguments[0]
		end = arguments[1]
	}
	// 截取之后,新生成的数组
	var tmpArr = []
	for(int i = start; i < end; i++){
		tmpArr.push(this[i])
	}
	return tmpArr
}

之所以要像前面一个弄得这么复杂是因为

forEach是ECMAScript2015里的语法,不兼容IE8

而 jQuery 2.0以下的版本是可以兼容IE8的,

jQuery的each方法主要是用来遍历jQuery中的伪数组的,

但是也可在IE8等低版本环境中,用来替代forEach方法

但是必须要将jQuery的伪数组先转换成原生的数组才行

这个转换过程,用的就是上面的代码:

;[].slice.call($(‘div’)).forEach(function(item,index){})

Node天生就可以把url地址处理得非常精简而优雅,默认情况下所有的资源都不允许访问,除非开发者指定哪些可以被请求和访问
因此,Node必须要自己设计出url地址访问规则

302 临时重定向 浏览器不会记住,下次请求时依然会发两次请求
打个比方: 某个地段在施工,你需要绕行另一条远路;那么,在你不知道这条路上的施工啥时候完成的情况下,你每次都会先来这儿看一下

301 永久重定向 浏览器会记住,下次直接请求最终的location
打个比方,要过河,来到以前的小桥时,发现,我cao,桥没有了!这个时候你会绕行从另一座桥上过河,并且记住,下次直接从新的小桥过河,再也不会到旧的小桥这儿了

在Node中编写应用主要是:
1.使用ECMAScript
2.使用核心模块
如fs,http,os,url,path等
3.使用第3方模块(需npm安装)
如art-template等
4.使用自定义模块

什么是模块化,满足以下两点:
1.文件作用域
2.通信规则
加载
导出

CommonJS模块规范
在Node中的JavaScript,有一个非常核心的概念:模块系统
1.模块作用域
2.使用require方法加载模块,没有script标签
3.使用exports导出模块中的成员

默认情况下,一个文件中供外部使用的函数或变量(如addFunction) 默认是 放在了 exports对象 里的成员中(如exports.addFunction = addFunction),
外部文件通过var exportsObj = require(‘foo.js’)之后,
才可以通过exportsObj.addFunction进行使用

但是,如果我想把一个文件中的函数直接提供给外部使用(即不是以挂载的方式提供给外部,并且外部也不使用对象.函数名 这样点语法的调用形式),那么可以在文件中使用 module.exports = addFunction
这样,外部文件通过 var addFunction = require(‘foo.js’)之后,直接就拿到了 foo.js内部的提供的函数了

node_22.js代码如下:

function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};};  

// 必须使用./
var exportAddFunction = require('./node_22_a.js')
NSLog('exportAddFunction: \n' + exportAddFunction)

被引用的node_22_a.js如下:

function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};};  


function addFunction (a,b) {
	return a + b
}

// 如果想直接提供函数或变量给外部 使用
// 必须使用以下方式
module.exports = addFunction

运行效果如下:

require的两个作用
1.执行被加载模块的代码
2.得到被加载模块的导出对象(如果使用的是module.exports = xxx,则得到的是被加载模块导出的函数或字面量)

exports的两种用法
1.Node是模块作用域,默认文件中所有成员只有当前文件中有效
2.如果希望导出成员时,有两种情况
2.1.导出多个成员
“`javascript
exports.girlName = ‘面码’
exports.showGirl = function(){
NSLog(‘Is Life Always Hard,Or Just When you are a kid ?’)
}
“`

2.2.导出单个成员 (在这种module的情况下会覆盖,以最后一个为准)

module.exports = ‘hello beyond’

module.exports = {girlName:’mathilda’,  girlAge:12}

module.exports = function(){  NSLog(‘这个杀手不太冷’)  }

示例代码如下:

node_23.js

function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};};  

// 必须使用 ./
var moduleExports = require('./node_23_a.js')
NSLog('moduleExports: \n' + moduleExports)

被引用的node_23_a.js

function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}  

// 导出单个成员 (在这种module的情况下会覆盖,以最后一个为准)
module.exports = 'hello beyond'
module.exports = {girlName:'面码',girlAge:13}
module.exports = function(){  NSLog('未闻花名')  }

运行效果如下:

关于exports与module.exports的实质:

module.exports原理分析:

在每一个模块中,内部都有一个module对象
该module对象中,有一个成员exports,默认是一个空对象
伪代码如下
var module = {
exports: {

}
}
在每一个模块的最后,都有一句
return module.exports代码

为了简化用户的代码和操作

模块中又有这样一句代码,为module.exports创建了一个引用exports而已

var exports = module.exports

这样一来 exports === module.exports 为true了
用户就可以使用exports代替module.exports了
例如:
exports.addFunction = function(a,b){return a + b}
就等价于
module.exports.addFunction = function(a,b){
return a + b
}

但是,如果仅仅只想导出一个函数或字符串,而不想导出一个对象的时候
exports = ‘hello beyond’
上面的代码,将变量exports重新指向了一个字符串,
因此,在模块最后的那一句代码
return module.exports 依然返回的是一个对象

因此,这种情况下,只能不偷懒,直接使用module.exports = ‘hello beyond’了

总之,无论是哪种情况,不管是 exports = ‘未闻花名’
还是module.exports = ‘那朵花’
只要是重新赋值了,exports变量与module.exports就再也没有联系了

模块的最后,始终return的是module.exports

如果妳实在是分不清exports 和 module.exports,
那么,请只使用module.exports即可

require 加载规则

1.优先从缓存中加载,
意思是:只要已经加载过一次了(代码已经被执行了一次),就不会再重新执行代码了
因为:require的最终(主要)目的是为了得到模块里的module.exports,
并不是为了反复多次重复执行模块内的代码才require的

这样做的好处是:提高模块加载效率,避免重复加载

演示代码node_25.js如下

	function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};};  

	// require 加载规则

	// 1.优先从缓存中加载,
	// 意思是:只要已经加载过一次了(代码已经被执行了一次),就不会再重新执行代码了
	// 因为:require的最终(主要)目的是为了得到模块里的module.exports,
	// 并不是为了反复多次重复执行模块内的代码才require的

	// 这样做的好处是:提高模块加载效率,避免重复加载


	// 必须使用 ./
	var moduleAExports = require('./node_25_a.js')
	var moduleBExports = require('./node_25_b.js')
	NSLog('moduleAExports: ' + moduleAExports,false)
	NSLog('moduleBExports: ' + moduleBExports)

node_25_a.js模块A代码如下:

function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}  

NSLog('模块A被加载了',false)
require('./node_25_b.js')
module.exports = exports === module.exports
NSLog('模块A加载完成',false)

node_25_b.js模块B代码如下:

function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}  

NSLog('模块B被加载了',false)
module.exports = exports === module.exports
NSLog('模块B加载完成',false)

运行效果如下:

require 模块加载规则(机制)

2.标识符分析 (共3种情况)
2.1. 核心模块
核心模块本质也是文件,但已经被编译到二进制文件中,只需按名字来加载

2.2. 第3方模块
所有的第3方模块必须使用npm 安装
通过require(‘包名’)进行加载,方可使用
不存在与核心模块同名的第3方模块(不允许)
对于不是核心模块,也不是自定义模块(路径形式)的第3方模块
以require(‘art-template’)为例,查找方式如下:
2.2.1
第1步,查找当前源代码所在目录下的node_modules目录
第2步,再找node_modules目录下的art-template目录
第3步,再找art-template目录下的package.json文件
第4步,再找package.json文件中的main属性
第5步,main属性中的值,就记录了第3方模块art-template的入口js模块: “main”: “index.js”,
最终加载这个js文件!实质上加载的还是文件

特殊情况1: 如果main属性值所对应的js文件不存在,或者没有main这个属性,再或者压根就没有package.json这个文件,那么,会尝试去找index.js这个备胎文件

特殊情况2: 如果连index.js这个备胎文件都没有,那么会进入上一级目录的node_modules目录中按上述步骤查找…直至回溯到磁盘根目录,最后报错 can not find module xxx

一般情况下:一个项目只会在根目录有一个node_modules文件夹,这样所有的子目录都能访问到

插一句:jQuery的源代码有一万多行,其实也是按不同的模块进行编写,最后才通过打包编译

2.3. 自定义模块(路径形式,必须有./或者../开头 或者/开头,结尾的.js可以省略)

自定义第3方模块演示如下:

先在node_modules目录下新建beyond文件夹

然后在beyond目录中新建package.json文件

在package.json文件中的内容如下:

{    
    "main" : "beyond.js"
}

同时,在beyond的目录中,新建beyond.js 和 一个备胎文件index.js

beyond.js内容如下:

console.log('this is beyond.js')

备胎文件index.js内容如下:

console.log('this is index.js')

node_26.js代码如下:

function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}  

NSLog('模块beyond被加载了',false)
require('beyond')
NSLog('模块beyond加载完成',false)

运行效果如下:

现在,我们把node_modules目录下的beyond文件夹下的package.json删除,

此时node会自动去找备胎文件index.js,运行效果如下:

推荐了一篇文章:深入浅出Node.js(三) 深入Node.js的模块机制(该书的模块系统章节)

npm相关细节 以及包描述文件
全称node package manager

建议:
1.在项目根目录下,使用npm init,创建包描述文件package.json

2.每次下载安装第3方包的时候,都使用 –save,将第3方包的信息写入package.json

3.如果某天不小心把node_modules目录删除了,但是只要package.json还在,就可以通过npm install命令重新下载所有的依赖的第3方包

同时安装多个第3方包 (使用空格隔开)
npm install art-template jquery bootstrap  –save

注意:  –save的作用是
往node_modules同级目录下的包描述文件package.json里面,添加记录 本项目中所依赖的外部第3方模块

上面讲过,这个与node_modules目录同级的package.json,可以通过npm init初始化出来

npm init命令的效果如下:

package.json内容如下:

{
  "name": "sg_node_source_code",
  "version": "0.0.1",
  "description": "this is sg_node source code ",
  "main": "node_0.js",
  "dependencies": {
    "art-template": "^4.12.2",
    "jquery": "^3.3.1"
  },
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "sg_node"
  ],
  "author": "beyond",
  "license": "ISC"
}

npm有两个含义
1. npmjs.com官方网站
里面可以搜索一些第3方的包,例如:Vue,Angular,React,Express,Boostrap,jQuery,Koa,etc

2. npm命令
在安装了node后,就自带了npm命令,

npm也有版本的区别,npm –version就可以查看

也可以通过以下命令,让npm自己升级自己

npm install –global npm

或者使用简写

npm i -g npm

下面是npm 常见命令介绍和演示

1. npm init 作用是在node_modules同级目录下生成包描述文件package.json

它还一个 可以跳过向导, 快速生成的命令:

npm init –yes

它的简写是: npm init -y

2. npm install

简写 npm i

该命令的作用是,在不小心删除了node_modules目录,但package.json文件还在的时候,

可以一次性,快速恢复所有的引用过的第3方包

3. npm install 第3方包

如npm install art-template

简写 npm i 第3方包

该命令的作用是,仅仅下载和安装第3方包,并不会记录到包描述文件package.json里

4. npm install 第3方包 –save (推荐使用)

简写(注意是大Snpm i 第3方包 -S

该命令的作用同样是: 安装第3方包,同时,它还会把下载的依赖 记录到包描述文件package.json里

5. npm uninstall 第3方包

简写 npm un 第3方包

作用是: 只删除安装过的某第3方包,但是依赖项仍然保存

6. npm uninstall 第3方包 –save

简写 npm un 第3方包 -S (注意是大写S)

该命令的作用是: 删除安装过的第3方包的同时,还删除掉依赖信息

7. npm help

示例:  npm help uninstall

该命令的作用是: 查看uninstall命令的使用帮助

注意: 在vim中,退出帮助文件时,使用的是  :wq

8. npm config list

该命令作用是: 查看 npm的配置信息

从下图可以看到,当前的下载源是 https://registry.npmjs.org/

解决:npm官网访问下载速度慢等问题

使用国内的 cnpm的镜像

先打开国内的淘宝镜像官网http://npm.taobao.org/

使用方法:

1.  安装cnpm

在任意目录下,执行命令: npm install –global cnpm

2.  使用cnpm代替npm安装第3方包 (npm依然可以使用,只是速度 比较慢 而已)

例如: cnpm install art-template –save

插入一句<深入浅出Node.js>翻了三遍(ynm)

核心代码:

如果不想下载cnpm,仅仅只是想用一下淘宝的镜像源,那么可以使用下面的命令进行安装第3方包:

npm install art-template –registry=https://registry.npm.taobao.org

但是,上面这种每次下载第3方的时候,都要手动指定下载的镜像源地址,很麻烦

也有一个一劳永逸的配置方法:

npm config set registry https://registry.npm.taobao.org

这样配置以后,将来每次下载第3方包,就全部从taobao的镜像源处下载了

就不再是以前的国外的https://registry.npmjs.org/了

Express Web 开发框架

官网expressjs.com

作者TJ,同时也是Koa作者,由于对Node错误机制的不满,现已转投Go的怀抱了

同样的Node.js作者 也投向了Go的怀抱

下面新建一个目录node_27

mkdir node_27

进入node_27目录

cd node_27

快速初始化 包描述文件 package.json

npm init -y

下载安装Express第3方包,并依赖写到包描述文件中

npm install express –save

整个过程效果如下:

将package.json中main属性值改成node_27.js,如下所示:

{
  "name": "node_27",
  "version": "1.0.0",
  "description": "",
  "main": "node_27.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.16.3"
  }
}

新建一个node_27.js作为入口文件

项目结构 如下:

var expressExport = require(‘express’)

运行效果如下:

node_27.js完整代码如下:

function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}  
// 导入框架
var expressExport = require('express')
// 创建服务器对象
var appServer = expressExport()
// 监听端口,并启动服务
appServer.listen(5267,function (error) {
	if (error) {
		return NSLog('启动失败: ' + error)
	}
	NSLog('服务启动成功')
})
// 处理get请求,至于请求参数可以这样:var queryObj = request.query
appServer.get('/',function (request,response) {  
	response.send('<html><head><title>beyond心中の动漫神作</title><link rel="icon" href="/public/img/beyond.jpg" /></head><body style="background-image:url(/public/img/sakura4.png);background-repeat:no-repeat;background-position:center center;"><h1 style="text-align:center;color:white;text-shadow:2px 2px 4px #000;letter-spacing:5px;">未闻花名</h1></body></html>')
})
// 开放静态资源(位于public目录)
var urlPrefix = '/public/'
var filePath = './public/'
var callbackFunction = expressExport.static(filePath)
appServer.use(urlPrefix,callbackFunction)

效果如下:

至于在express中如何使用模板引擎art-template,

在art-template官方网站中有详细介绍说明

附录:

# Node.js 第3天课堂笔记

## 知识点

– 增删改查
– 登陆
– 注册
– 头像
+ 服务端图片
+ 水印
+ 图片水印
– 找回密码
– 密码修改

– 模块系统
+ 核心模块
+ 第三方模块
+ 自己写的模块
+ 加载规则以及加载机制
+ 循环加载
– npm
– package.json
– Express
+ 第三方 Web 开发框架
+ 高度封装了 http 模块
+ 更加专注于业务,而非底层细节
+ 知其所以然
– 增删改查
+ 使用文件来保存数据(锻炼异步编码)
– MongoDB
+ (所有方法都封装好了)

## 反馈

–  再推荐一些前端学习的书籍
+  《JavaScript 高级编程》第3版
+  学习,解决问题
+  书本可以更好的系统的整理学过的内容,了解一些细节
+  《JavaScript 语言精粹》
– seo的资料,嘿嘿
+ 网站运营 SEO
+ SEO 运营专员
+ 百度、Google、搜狗、
+  软件开发版本里面涉及到软件工程学:
+  x.x.x
*  0.0.1
*  0.0.2
*  1.1.5
*  1.9.2
*  2(新增功能比较多,甚至可能去除了某些功能).5(加入了新功能).0(修复bug,提升性能)
*  大版本
*  一般是这些客户端软件、技术框架开发者比较理解的多
*  做网站很少涉及到版本的概念,网站的目的就是快
– art-template里面用的语法是jQuery吗, each什么的 我晕了 each,forEach, 遍历的全混了
+ art-template 和 jQuery 一毛钱关系都没有
+ each 是 art-template 的模板语法,专属的
+ {{each 数组}}
+ <li>{{ $value }}</li>
+ {{/each}} 这是 art-template 模板引擎支持的语法,只能在模板字符串中使用
+ $.each(数组, function)
+ $(‘div’).each(function) 一般用于遍历 jQuery 选择器选择到的伪数组实例对象
+ forEach 是 EcmaScript 5 中的一个数组遍历函数,是 JavaScript 原生支持的遍历方法 可以遍历任何可以被遍历的成员
+ jQuery 的 each 方法和 forEach 几乎一致
+ 由于 forEach 是 EcmaScript 5 中的,所以低版本浏览器不支持
+ 技多不压身
+ Node 对于前端来讲是进阶高级前端开发工程师必备的技能
+ 屌丝最容易逆袭的职业
+ 见得东西多了你就不怕了
+ 前后端融会贯通之后,真的可以为所欲为
–  老师讲的挺清晰的 可是第一节太困了 路径有点没转变过来
– 如果从a中调用b中的数据,又从b中调用a中的数据,执行a代码,为什么把b中的执行完后才会执行a,而不是在b调用a的时候a中的代码继续执行
+ a 加载了 b
* 执行 b 中的代码
* 同时得到 b 中导出的接口对象:exports
* 执行 b 的过程中发现 b 也在 require a
* b 就会反过来执行 a
* a 中又加载 b
* b 又反过来加载 a
* 这就是循环加载
* 如果你一旦出现了这种情况,说明你的思路有问题。
* jQuery.js (可能不可能出现 jQuery 依赖了 main)
* main.js 依赖了 jQuery
* 这个问题是矛盾。
+ b 中也加载了 a
+
+ 网页中所有的路径其实都是 url 路径,不是文件路径
– 问题就是不知道问题是什么,写案例的时候似懂非懂
– 感觉思维有点跟不上,

## 复习

– 网站开发模型
+ 黑盒子、哑巴
+ 写代码让它变得更智能
+ 按照你设计好的套路供用户使用

– 在 Node 中使用 art-template 模板引擎
+ 安装
+ 加载
+ template.render()
– 客户端渲染和服务端渲染的区别
+ 最少两次请求,发起 ajax 在客户端使用模板引擎渲染
+ 客户端拿到的就是服务端已经渲染好的
– 处理留言本案例首页数据列表渲染展示
– 处理留言本案例发表留言功能
+ 路径
+ 设计好的请求路径
+ $GET 直接或查询字符串数据
+ Node 中需要咱们自己动手来解析
* url.parse()
+ /pinglun?name=jack&message=hello
+ split(‘?’)
+ name=jack&message=hello
+ split(‘&’)
+ name=jack message=hello
+ forEach()
+ name=jack.split(‘=’)
+ 0 key
+ 1 value
– 掌握如何解析请求路径中的查询字符串
+ url.parse()
– 如何在 Node 中实现服务器重定向
+ header(‘location’)
* 301 永久重定向 浏览器会记住
– a.com b.com
– a 浏览器不会请求 a 了
– 直接去跳到 b 了
* 302 临时重定向 浏览器不记忆
– a.com b.com
– a.com 还会请求 a
– a 告诉浏览器你往 b
– Node 中的 Console(REPL)使用

## 上午总结

– jQuery 的 each 和 原生的 JavaScript 方法 forEach
+ EcmaScript 5 提供的
* 不兼容 IE 8
+ jQuery 的 each 由 jQuery 这个第三方库提供
* jQuery 2 以下的版本是兼容 IE 8 的
* 它的 each 方法主要用来遍历 jQuery 实例对象(伪数组)
* 同时它也可以作为低版本浏览器中 forEach 替代品
* jQuery 的实例对象不能使用 forEach 方法,如果想要使用必须转为数组才可以使用
* `[].slice.call(jQuery实例对象)`
– 模块中导出多个成员和导出单个成员
– 301 和 302 状态码区别
+ 301 永久重定向,浏览器会记住
+ 302 临时重定向
– exports 和 module.exports 的区别
+ 每个模块中都有一个 module 对象
+ module 对象中有一个 exports 对象
+ 我们可以把需要导出的成员都挂载到 module.exports 接口对象中
+ 也就是:`moudle.exports.xxx = xxx` 的方式
+ 但是每次都 `moudle.exports.xxx = xxx` 很麻烦,点儿的太多了
+ 所以 Node 为了你方便,同时在每一个模块中都提供了一个成员叫:`exports`
+ `exports === module.exports` 结果为  `true`s
+ 所以对于:`moudle.exports.xxx = xxx` 的方式 完全可以:`expots.xxx = xxx`
+ 当一个模块需要导出单个成员的时候,这个时候必须使用:`module.exports = xxx` 的方式
+ 不要使用 `exports = xxx` 不管用
+ 因为每个模块最终向外 `return` 的是 `module.exports`
+ 而 `exports` 只是 `module.exports` 的一个引用
+ 所以即便你为 `exports = xx` 重新赋值,也不会影响 `module.exports`
+ 但是有一种赋值方式比较特殊:`exports = module.exports` 这个用来重新建立引用关系的
+ 之所以让大家明白这个道理,是希望可以更灵活的去用它
– Node 是一个比肩 Java、PHP 的一个平台
+ JavaScript 既能写前端也能写服务端

“`javascript
moudle.exports = {
a: 123
}

// 重新建立 exports 和 module.exports 之间的引用关系
exports = module.exports

exports.foo = ‘bar’
“`

“`javascript
Array.prototype.mySlice = function () {
var start = 0
var end = this.length
if (arguments.length === 1) {
start = arguments[0]
} else if (arguments.length === 2) {
start = arguments[0]
end = arguments[1]
}
var tmp = []
for (var i = start; i < end; i++) {
// fakeArr[0]
// fakeArr[1]
// fakeArr[2]
tmp.push(this[i])
}
return tmp
}

var fakeArr = {
0: ‘abc’,
1: ‘efg’,
2: ‘haha’,
length: 3
}

// 所以你就得到了真正的数组。
[].mySlice.call(fakeArr)
“`

## 下午总结

– jQuery 的 each 和 原生的 JavaScript 方法 forEach
– 301 和 302 的区别
– 模块中导出单个成员和导出多个成员的方式
– module.exports 和 exports 的区别
– require 方法加载规则
+ 优先从缓存加载
+ 核心模块
+ 路径形式的模块
+ 第三方模块
* node_modules
– package.json 包描述文件
+ dependencies 选项的作用
– npm 常用命令
– Express 基本使用

– 使用 Express 把之前的留言本案例自己动手改造一下

未完待续,下一章节,つづく