node初识

Wenn 于 2022-09-27 发布

node在vscode终端输入输出

//首先如果想在终端中输入参数,则需要开启全局对象process
const a = process.args[2]
const b = process.args[3]
console.log(a,b)
//num=1 num=2
//上面全局对象的变量index从2开始,第0项为node运行地址,第1项为所运行文件地址
$ node xx.js num=1,num=2
node程序传递参数
$ node xx.js args[0],args[1],...args[n] 直接追加在后面
console.log()   输出
consoel.clear()  清除控制台  cls
conole.tract()   打印函数调用栈

异常处理

当我们的代码输入不符合我们所要求的类型时,我们可以抛出错误来阻断代码执行

function (type){
    if(type === 0)throw new Error()   //如果type为0,则抛出错误
}

当抛出错误时,如果最近一层的对象不进行处理,则继续向层报错,上层捕获到错误后,如果不进行处理,则继续向上抛出错误,以此类推,直至到最顶层调用者,独断后续代码执行

function foo(){
    throw new Error   //抛出错误
}
function bar(){   //捕获错误但不进行处理,则继续向上抛出
    foo()
}
function test(){   //捕获bar抛出的错误,不进行处理,继续向上抛出
    bar()
}
test(); //最顶层调用者捕获test抛出的错误,阻断后续代码执行

当代码如果在抛出错误时需要执行一些必须的操作,则可以使用try{}catch(){},此时并不阻断后续代码的执行,仅当出现错误时执行对应的代码块

function foo(){
    try{  //执行可能会发生错误的代码
        throw new Error
    }catch(err){   //捕获try中出现的错误,执行自身的代码,但并不阻断后续代码的执行
        alert(err.message)
    }
}

如果不论抛出或者捕获都要进行的代码块时,则可以使用finally来进行

function foo(){
    try{
        throw new Error
    }catch(err){
        console.log(err)
    }finally{
        console.log('不论如何,不影响我的执行')
    }
}

模块化开发

  1. 最简单的导入导出方式

    //第一个js文件 ex:why.js  该文件导出
    const name = 'xi'
    const age  = 20
    const sum = (num1,num2)=>{
        return num1+num2
    }
    module.exports = {
        name,
        age,
        sum
    }
    
    //第二个文件  导入  使用why.js的参数
    const why = require('./why.js')
    console.log(why.name)
    console.log(why.age)
    console.log(why.sum(20,30))
    

    当我们使用exports进行暴露时,我们暴露的是一个对象,此时对象指向我们所要暴露的变量,require是一个函数,它以我们所暴露出来的文件名进行查找,并返回所查找的对象,那次是第二个文件中的why在内存中也指向索要暴露出来的变量

    //第一个文件  ex:why.js  该文件导出
    const name = 'xi'
    const age = 20
    const sum = (num1,num2)=>{
        return num1+num2
    }
       
    exports.name = name
    exports.age = age
    exports.sum = sum
    //当我们使用exports进行暴露时,依旧可以进行导出,但并不能直接导出一个对象
    ex: exports = {
        name,
        age,
        sum
    }
    //此时并不会进行导出,主要是因为源码中的exports是指向moudule.exports
    module.exports = {}
    exports = module.exports  //此时exports指向moudle.exports
    //也就是最终进行导出的依旧是moudle.exports
    

    require查找规则

    基本格式为require(X)

    • 如果X是一个 Node核心模块,比如pathhttpfs => file system 此时直接返回核心模块,并停止查找

    • 如果X是以./or../or/根目录开头的

      1. 首先将X作为一个文件在对应的目录下查找:

        1.1 如果有后缀名,按照后缀名的格式查找对应的文件

        1.2 如果没有后缀名,则会按照以下的顺序进行查找:

        + 直接查找文件X;
        + 查找X.js文件
        + 查找X.json文件
        + 查找X.node文件
        
      2. 如果没有找到对应的文件,则将X作为一个目录,查找目录下面的index文件

        2.1 查找X/index.js文件

        2.2 查找X/index.json文件

        2.3 查找X/index.node文件

      如果没有找到,则报错not found

    • 如果X不是一个核心模块,且不是一个文件路径,则开始在文件的node_modules文件夹下找对应的index,如果没有找到,则向上层找,依次向上,如果找不到则not found

    模块的加载过程

    • 模块在被第一次引入时,模块中的js代码会被运行一次

    • 模块被多次引入是,会缓存,最终只加载(运行)一次

      每个js文件对应一个module的实例,每个模块对象module都有一个属性:loaded

      为false标识还没有加载,为true便是已经加载

    • 如果有循环引入,则按照程序的执行顺序进行加载

      它是一种图结构,在遍历过程中先进行深度优先搜索(DepthFirstSearch),之后在进行广度优先搜索(BreathFirstSearch)

    ES Moudle

    平时使用我们必须使用live server来运行代码,因为modle并不支持file协议,支持httphttps协议,所以当我们在本地运行代码时,此时的url 协议为file

    <-- 主要文件引入主要模块文件 -->
    <script src='main.js'></script>
    
    //主要js文件,引入次要模块文件
    import {name,age,sum} from "./foo.js"
       
    //导入时也可以按照as给他们起别名
    import {name as fname,age as fage,sum as fsum} from "./foo.js"
       
    //也可以将导出的所有内容放到一个标识符中
    import * as foo from './foo.js'
    
    //模块js文件
    export  const name = 'xi'
    export  const age = 20
    export  const sum = (num1,num2)=>{
                    return num1+num2
                 }
       
    //第二种导出方式
    const name = 'xi'
    const age = 20
    const foo = function (){}
    export {  //这里的export并非一个对象,里面存放的也是所有的变量标识符
    	name,
        age,
        foo
    }
       
    //第三种,也可以给他们起别名,使用as;当然此时我们导入时需要将对应导入文件的变量名修改为别名,使他们一一对应
    export {
    	name as fName,
    	age as fAge,
        foo as fFoo
    }
    

    当我们的js文件所使用的过多时,则可以将所有同文件下的js文件导入index.js,这样我们在主文件中只需要导入对应的index文件即可

    //ex: index.js
    function foo(){}
    export {foo} from './math.js'
    //这样我们也可以直接将我们math文件导入index文件,并让index文件导出,使得主文件可以直接导入
       
    //当我们文件中所有的的变量都需要被导入时,则直接使用通配符导出,此时对应文件的所有变量都可以导入该文件了
    export * from './math.js'
    

    当我们如果在导入时,不进行大括号,那则导入的时默认的导出;默认导出只有一个

    function foo(){}
    //第一种,在export中使用别名
    export {
    	foo as default;
    }
    //第二种 直接使用export defalut 
    export default foo; //此时将foo作为默认导出
       
    import foo from "./math.js"  //默认导入
    

    import的使用,import函数返回的是一个Promise

    我们如果有需要在js文件导入成功之前就需要执行的代码,那我们就需要进行异步操作;

    使用import("./").then来进行异步操作

    import("./math.js").then(res=>console.log(res))
    console.log('提前执行的代码')   
    //此时的代码会再导入js之前就运行,不会等待js文件加载
    

    ES11新增的特性

    我们可以将import当作一个对

    console.log(import.meta)   //此时输出js文件所在的路径
    

ES Module解析过程

一、 构建,根据地址找到对应的js文件,下载,之后浏览器对其解析(静态分析)为模块记录(数据结构)

二、 实例化,对模块记录进行实例化对象,并且分配内存空间,解析模块中的导入导出语句,把模块指向对应的内存地址; 仅解析importexport,其余代码会在运行时在进行解析

三、 运行,运行代码,计算值,赋值(将值填到对应的内存地址中)

在webpack环境中,我们可以将esmodule和commonjs互相引用

npm使用run和不使用run没有区别,他们是等价的;

package.json中常见的属性

npm install命令

安装过程

image-20220928171236111

如果安装时出现最新版本高于lock版本,则会更新lock版本

npm uninstall xxx卸载

npm rebuild 强制刷最新build

npm cache clean清除缓存

image-20220928180536732

set基本使用

在ES6之前,我们的存储结构主要有两种:数组、对象

Set类似于数组,但和数组最大的区别是元素不能重复

//数组
const arr = new Array(1,2,1) //[1,2,1]

//Set
const set = new Set(arr)  //Set(2) { 1, 2 }  Set数据可以使用展开运算符
//set添加数据
const set = new Set()
set.add(10)
console.log(set)  //Set(2) { 10 }

const a = {}
const b = {}
set.add(a)
set.add(b)
console.log(set)   //Set(2) { {},{}}

const c = {}
const d = c
set.add(c)
set.add(d)
console.log(set)   //Set(1)  { {} }
//个数
console.log(set.size)  // 2

set.add(30)  //添加
set.delete(30)   //删除
set.has(20)  //是否包含
set.clear()   //清楚所有
set.forEach   //遍历
for(const item of set){}  //for-of遍历

WeakSet

  1. 只能存放对象类型
  2. 弱引用,如果没有其他引用该对象,则会被GC回收
  3. 无法遍历
const set = new Set()
let obj = {name:'xi'}   //0x100
set.add(obj)
obj = null  //此时0x100并不会被回收

const weakset = new WeakSet()
let obj2 = {name:'an'}   //0x200
weakset.add(obj2)
obj2=null   //此时0x200会被GC回收