node_01_javascript模块化

Node程序传递参数

正常情况下执行一个node程序,直接跟上我们对应的文件即可

node index.js

但是在某些情况下执行node程序的过程中,我们可能希望给node传递一些参数

node index.js env=development codewhy

如果我们这样来使用程序,就意味着我们需要在程序中获取到传递的参数

  • 获取参数其实是在process内置对象中的
  • 直接打印这个内置对象,他包含着特别的信息,比如版本,操作系统信息
  • 我们找到一个argv属性,他是一个数组,里面有我们传递的参数
    在这里插入图片描述
  • ps:argv:argument vector的缩写,传入的具体参数,vector翻译过来是矢量的意思,在程序中表示的是一种数据结构,在c++和Java是一种数据结构,在javascript也是这种结构

Node程序的输出

console.log()//打印
console.clear()//情况控制台
console.trace()//打印函数的调用栈
更多的一些console方法,在官网有

http://nodejs.cn/api/console.html更多console方法

特殊的全局对象

  • 包括__dirname ,__filename,exports,module,require()
  • 这些对象实际上是模块中的变量,只是每个模块都有,看起来像全局变量
  • 这些全局对象在命令行中是不可以使用的
console.log(__dirname) //路径名字
console.log(__filename)//文件名字
setTimeout(() => {
    console.log(setTimeOut)
}, 1000);
setInterval(()=>{
    console.log(setInterVAL)
})
setImmediate(()=>{
    console.log(setImmediate)
})

模块化

什么是模块化

  1. 事实上模块化开发最终的目的是将程序划分为一个个小的结构
  2. 这个结构中编写属于自己的逻辑代码,有自己的作用域,不会影响到其他的结构
  3. 这个结构可以将自己希望暴露的变量,函数,对象等到处给其结构使用
  4. 也可以通过某种方式,导入另外结构中的变量,函数,对象等
  5. 这种按照一个个结构,模块开发的过程就叫做模块化开发
  6. 早期的javascript,存在着很多的缺陷:比如var定义的变量作用域问题,比如javascript的面向对象并不能像常规面向对象一样使用class,比如javascript没有模块化的问题,这些缺陷问题都基本的完善,目前还存在不完善的明细地方就是静态类型检测,这也是为什么typescript有市场的原因

早期的javascript

  • 在网页开发的早期,javascript仅仅作为一种脚本语言,做一些简单的表单验证活动画实现等,那个时候的代码还很少,只需要将javascript代码写到script标签中即可,有时候只有几行
  • 随着前端和Javascript的快速发展,ajax的出现意味着后端返回数据后,我们需要通过javascript进行前端页面的渲染,SPA的出现。前端页面变的复杂;包含前端路由,状态管理等等一系列复杂的需求通过javascript进行前端页面的渲染
  • 包含Node的实现,javascript编写复杂的后端程序,没有模块化是致命的硬伤
  • 所有在没有es6模块化之前,有很多不同的模块化规范,AMD,CMD,CommonJS等

没有模块化的问题

//foo.js
var name="pyk"
//bar.js
var name="kyp"

如果同一个脚本引入foo和bar会造成变量污染,name的值不是开发者预期的,这就是没有模块化的缺陷,当然,也可以使用自执行函数实现模块化

(function(){
	var name="pyk"
})()

但是由此带来了一些问题

  • 开发者必须要记住每一个模块中返回对象的命名,才能在其他模块使用过程中正确的使用
  • 代码写起来混乱不堪,每个文件中的代码都需要包裹在一个匿名函数中编写
  • 在没有良好的规范下,每个人都有可能任意命名,出现模块名称相同的情况
    这就有了AMD,CMD,CommonJS等规范

CommonJS和Node

我们需要知道CommonJS是一个规范,最初提出来是在浏览器以外的地方使用,并且当时被命名为ServerJS,后来为了体现他的广泛性,修改为CommonJS,平时我们也会简称CLS

  • Node是CommonJS在服务器端一个具有代表性的实现
  • Browserify是CommonJS在浏览器的一种实现
  • webpack打包工具具备对CommonJS的支持和转换
    所以Node中对CommonJS进行了支持和实现,让我们在开发node的过程中可以方便的进行模块化开发
  • 在node中每一个单独的js文件都是一个单独的模块
  • 这些模块中包括CommonJS规范的核心变量;exports,module.exports,require
  • 我们可以使用这些变量在Node方便的进行模块化管理 exports,module.exports是导出模块,requirew是导入模块
//bar.js
const name="codewhy"
const age=18;
let message="my name is why"
function sayHello(name){
	console.log("Hello"+name)
}
//每个模块都有一个exports对象,是一个空对象,
exports.name=name
exports.age=age
exports.message=message
exports.sayHello=sayHello

//main.js
const bar=require('./bar')//返回的对象是exports,实际是将bar模块的内存引入地址赋值给变量bar,模块的变量改变,main里面导入的也会跟着变化
console.log(bar.name)
console.log(bar.age)
console.log(bar.message)
bar.sayHello()

const {name,age,sayHello}=require('./bar')//es6的对象结构
console.log(name)
console.log(age)
console.log(message)
sayHello()

module.exports

  • module.exports和exports的关系
  • CommonJS中是没有module.exports的概念的
  • 但是为了实现模块的导出,Node中使用的是Module类,每一个模块都是Module的一个实例,也就是module
  • 所以在Node中真正导出的其实根本不是exports,而是module.exports
  • 因为module才是导出的真正实现者
module.exports=exports  //因此exports才可以导出
module.exports=exports=bar //他们三个都指向同一个内存地址

//bar.js
const name="codewhy"
const age=18;
let message="my name is why"
function sayHello(name){
	console.log("Hello"+name)
}
exports.name=name
exports.age=age
exports.message=message
exports.sayHello=sayHello
//赋值了一个新的对象,新对象的内存地址覆盖了原本的,导出的就是新的对象,上面exports导出的则不存在,改变exports的值也无法改变导出的值,因为exports不再是导出的对象了
module.exports={

}

内存理解module.exports

let name="pyk"
setTimeout(()=>{
	name:"哈哈哈哈"
},1000)
//由于是值引用,一秒钟后也不会更改name的值,还是导出pyk
module.exports:{
	name:name
	age:123,
	sayHello:functio(name){
		console.log(name)
	}
}

let info={
	name:"pyk"
}
setTimeout(()=>{
	info.name:"哈哈哈哈"
},1000)
//是对象引用,一秒钟后导出的info里面的name会变成哈哈哈哈
module.exports:{
	info:info,
	age:123,
	sayHello:functio(name){
		console.log(name)
	}
}

小细节

//bar.js
//exports={}
//module.exports=exports
exports=123;
//main.js
console.log(require('./bar'))//打印得知exports等于{}空对象,则知道module.exports=exports是在模块最顶层

require细节

现在我们已经知道 require是一个函数,可以帮助我们引入一个文件中导入的对象
常见的的查找规则:导入格式如下:

require(X)
情况一:X是一个核心模块,比如path,http
直接返回核心模块,并且停止查找
情况二:X是以./../开头的或(根目录)开头的
第一步:X当做一个文件在对应的目录下查找
1.如果有后缀名,按照后缀名的格式查找对应的文件
2.如果没有后缀名,会按照如下顺序:
 *直接查找文件X
 *查找X.js文件
 *查找X.json文件
 *查找X.node文件
 第二步:没有找到对应的文件将X作为一个目录
 1.查找目录下面的index文件
 	*查找X/index.js文件
 	*查找X/index.json文件
 	*查找X/index.node文件
 	如果没有找到,那么报错:not found
情况三:直接是一个X(没有路径),并且X不是一个核心模块
	例:require('第三方包')//node_module里面查找,没有的的话就返回上一个目录的node_module里面查找,一直查到根目录如果没有找到,那么报错:not found
		

CommonJS规范缺点

  • CommonJS加载模块是同步的,意味着只有等到对应的模块加载完毕,当前模块中的内容才能被运行,这个在服务器上不会有问题,因为服务器加载的js都是本地文件,加载速度非常快
  • 浏览器加载js文件需要先从服务器将文件下载下来,之后在加载运行;那么采用同步就意味着后续的js代码可能无法正常运行,即使是一些简单的dom操作,但是webpack会将CommonJS的代码进行转换,变成浏览器可执行的代码,所以这个也不是问题
  • 在早期浏览器为了使用模块化,通常会采用AMD和CMD, 但是目前一方面现在的浏览器已经支持ES Modules,另一方面借助于webpack等工具可以实现对CommonJS或者ES Module代码的转换了,AMD和CMD很少用了,了解一下即可

AMD规范

  • AMD是Asynchronous Module Definition(异步模块定义)的缩写
  • 它采用的是异步加载模块
  • 事实上AMD的规范还要早于CommonJS,但是CommonJS,但是CommonJS目前还有少量使用,不过很少很少了
  • AMD实现的比较常用的库是require.js和curl.js,需要下载require.js在script标签引入 并加上属性 data-main="./index.js"
//index.js
(function(){
	require.config({
	baseUrl:'',
	paths:{
		"bar":"./modules/bar",
		"foo":"./modules/foo"
	}
	})
})

//main.js
<script data-main="./index.js"></script>
//bar.js
define(function(){
	const name="codewhy";
	const age=18;
	const sayHello=function(name){
	console.log("你好"+name)
	}
	return{
	name,
	age,
	sayHello
	}
})
//foo.js
define(['bar'],function(bar){
	console.log(bar.name)
	console.log(bar.age)
	bar.sayHello('pyk')
})

CMD规范

*CMD规范也是应有于浏览器的一种模块化规范

  • CMD是Common Module Definition的缩写
  • 它也才用了异步加载模块,但是它将CommonJS的优点吸收过来
  • 但是目前使用的也非常少
  • 实现方案SeaJS 下载引入
//main.js
<script src="./sea,js"></script>
<script>
	seajs.use('./index.js')
</script>
//foo.js
define(function(require,exports,module){
	const name="李银河";
	const age=18;
	const sayHello=function(name){
	console.log("你好"+name);
	}
	module.exports={
	name,
	age,
	sayHello
	}
})
//index.js
defile(function(require,exports,module){\
	cosnt foo=require('./module/foo')
	console.log(foo.name)
	console.log(foo.age)
	console.log(foo.sayHello)
})

ESModule

  • Javascript没有模块化一直是它的痛点,所以才会产生那么多的社区规范:CommonJS,AMD,CMD,所以ES推出自己的模块化系统,是非常有用的
  • ESModule和CommonJS的模块化有一些不同之处
  • ESModule模块采用export和import关键字来实现模块化
  • export负责将模块内的内容导出
  • import负责从其他模块进行导出
//常见导出方式三种
1.方式一:
export const name="why"
export const age=18
export cosnt sayHello=function(name){
	console.log(name)
}
2.方式二:
//{}是大括号,但不是一个对象,不可以写键值对
//大括号要放置导出的变量的内存地址引用列表
export{
	//name:name,错误写法,不是对象无法用键值对
	name,
	age,
	sayHello
}
3.方式三:{}导出时,可以给变量起别名,也印证了{}不是对象
export{
name as fName,
age as fAge,
sayHello as fSayHello
}

前面的几种导出都是有名字的导出(name exports)在导出的时候都指定了名字,在import导入时要知道名字,下面这个就不用,导入也不需要知道名字.
default export(默认导出)
//bar.js
// 默认导出format,这里的format名字要不要都可以,注意,默认的只能有一个
export  default function format(){
	console.log('对某一个东西进行')
}
//index.js
//导入default,任意命名 
import form from './bar.js'

常见导入方式三种
第一种导入:{}不是对象,变量要和导出的变量名一样,路径要加js,webpack会默认加,没有使用webpack就要自己加.js
import {} from './module/foo.js';
第二种导入:拿到导出的name其别名,为Fname,如果导出的时候已经设置别名,那就用导出的别名再设置别名
import{name as FName,age as FAge } from './module/foo.js'
第三种导入: *as foo 将全部的导出模块放在foo对象里面
import *as foo from './module/foo.js'

exportimport结合使用,导入再导出
export {name,age,sayHello} from './foo.js'
用途:在开发和封装一个功能库的时候,通常我们希望将暴露的所有接口放到同一个文件中,这样既方便统一接口规范,也方便阅读
这个时候,我们就可以使用exportimport结合使用
  • 注意:import xx from 'xxx’函数是不可以放在逻辑代码里面的,因为js在解析的时候就已经确定了依赖关系了,require()是可以在逻辑代码里面用的,因为他本质是一个函数,运行时才编译
let flag=true;
if(flag){
	import pyk from './pyk' //这种写法是错误的,import()函数是不可以放在逻辑代码里面的
	const kkk=require('kkk.')//可以这样子写
	如果有场景是纯ESModule的环境:import()可以如下使用,返回一个promise
		import('./foo.js').then(res=>{
		console.log(res.name)
		
	}).catch(err=>{
	})
	用途:首屏加载太多js会卡,加快首页渲染速度
}

ESModule加载过程

ESModule加载js文件的过程是编译时加载的,并且是异步的
给script标签加type=“module”,相当于给他加async,不会阻塞后面的代码

<script type="module"></script>
  • 因为ESModule导出的本质就是创建一个模块记录环境(Module environment record)并把内存地址引用给复制导出,而在模块记录环境里面是类似于const name=“pyk”,每次在导出环境的时候修改就会进行删除cosnt name=“pyk”,再重新添加,实时动态绑定到导入模块。
  • 导入模块里面修改不了const的值,无法修改模块环境记录里面的值,但是我们要想实现类似效果,可以在导出模块里面进行声明一个变量对象,把需要在导入模块可以修改的值,放对象里面,然后导出他,在模块记录环境里面会在这个对象的引用也导出,导入模块也可以修改了
let info={
	myName:"pyk"
}
let age=18;
const sayHello=function(name){
	console.log(name)
}
export{
	info,
	age,
	sayHello
}

//main.js
import {info,age,sayHello}
setTimeout(()=>{
info.myName="HAHAHA"
	console.log(info.myName)//打印HAHAHA,因为上面的导出不是值引用,是对象地址引用,才可以更新
},2000)
//bar.js
let name="pyk";
let age=18;
const sayHello=function(name){
	console.log(name)
}
setTimeout(()=>{
	console.log(name)//可以修改
},1000)
export{
	name,
	age,
	sayHello
}

//main.js
import {name,age,sayHello}
setTimeout(()=>{
 name="你好"//无法改变bar.js的值,报错
},2000)

Node对ESModule的支持

  • 在最新的Current版本(v14.13.1)中,支持ES Module我们有以下两种方法
  • 方式一:在package.json中配置type:module(后续补充)
  • 方式二:文件以.mjs结尾,表示使用的是ES Module;
//bar.mjs
let name="pyk"
let age=18
export{
name,
age
}
//main.mjs
import {name,age} from bar.mjs
//命令行
node main.mjs

CommJS和ES Module交互

  • 结论一:通常情况下,CommonJS不能加载ES Module
  • 因为CommonJS是同步加载的,但是在ES Module必须经过静态分析等,无法在这个时候执行javascript代码
  • 但是这个并非绝对的,某些平台在实现的时候可以对代码进行针对性的解析,也可能会支持
  • Node当中是不支持的
  • 结论二:多数情况下,ESModule可以加载CommonJS
  • ES Module加载CommonJS时,会将module.exports导出的内容作为default
    导出方式来使用
  • 这个依然需要具体的实现,比如webpack是支持的,Node最新的current版本 也是支持的
  • 但是在最新的LTS版本也支持
//bar.js
const name="pyk"
module.exports=name
//main.mjs
import foo from './bar.js'
console.log(foo.name) //打印出pyk,支持
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了python应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值