• 我的Vue之旅、02 ES6基础、模块、路径、IO


    自定义模块

    为什么要模块?模块化源代码能给我们带来什么好处?

    试想一个巨无霸网购平台,在没有模块化的情况下,如果出现bug,程序员就要在几百万行代码里调试,导致后期维护成本上升,为了解决问题,模块化按功能切分,把大问题转换成小问题,让每个模块独立运营,通过接口对外开放,让程序统一调用,降低程序出错的风险,也能方便升级模块内部的代码,不影响全局

    创建模块(🍔计算器)

    模拟巨无霸程序(现有两个js文件 app.js server.js)

    如果没有模块化程序,那么server.js也想用这些功能的时候也必须将函数写一遍。就会出现代码重叠,如果要修改,就要全部进行修改
    ⭐ 所以我们将这部分函数模块化

    1. 新建calculator.js,将 app.js 的函数剪贴到其中(此时再运行app.js会出错)

    2. 我们需要在 app.js 中引入我们新建的模块打印出来了一个 { } 空 Object 对象,⚠️ 这是因为 calculator.js 里的函数是不会自动暴露或者被输出的,我们需要明确告诉NodeJS哪一个函数需要被输出,那样才能被其他程序引入和调用

    1. 模块使用 module.exports 语句,在运行 app.js console输出函数 add

    1. 😅 那 subtract 怎么办呢,因为module.exports={ },我们可以往里添加新数据,我们只需要提供一对 key-value 即可

    1. console输出两个函数 add, subtract
    // app.js
    const calculator = require('./calculator.js');
    
    console.log(calculator);
    
    let addResult = calculator.add(1,2);
    
    console.log(`1 + 2 = ${addResult}`);
    

    const calculator = require('./calculator.js');
    
    console.log(calculator);
    
    let SubtractResult = calculator.subtract(5,2);
    
    console.log(`5 - 2 = ${SubtractResult}`);
    

    function add(num1,num2){
        return num1 + num2;
    }
    
    function subtract(num1,num2){
        return num1-num2;
    }
    
    //module.exports.add = add;
    //module.exports.subtract = subtract;
    
    //exports.add = add;
    //exports.subtract = subtract;
    
    module.exports = {
        add,  //add:add,
        subtract     //subtract:subtract
    }
    

    💡 自定义模块小技巧

    • ⭐ 如果我们有很多模块,重复输入 module.exports 是很麻烦的,NodeJS给我们提供了一个便利,直接把最前面的 module 去掉
    • ⭐ 我们可以写成 module.exports = { 内容 } 的形式来简化,如果key-value相等,可以只写key

    🖥️ CMD终端的基本使用

    sysdm.cpl  可快速打开计算机设置配置环境变量
    md abc 	创建目录 abc
    rd abc 	删除目录 abc
    cd ..	进到上层目录 
    
    如果目录不是空的不能用rd删除目录,需要加两个参数
    rd /s/q abc   (sub-directory quiet)
    
    echo on a.txt 创建 a.txt
    往里面覆盖内容
    echo 123 > a.txt
    往里面追加内容
    echo 456 >> a.txt
    
    cat a.txt 输出文件内容
    
    rm a.txt 删除文件
    
    cat > hello.txt 覆盖(按ctrl c 退出)
    cat >> hello.txt 追加内容(按ctrl c 退出)
    
    在文件内按 Shift 右键 选择 在该目录下执行命令行
    

    Node.JS 全局成员

    console.log(123);
    
    // 包含文名称的全路径
    console.log(__filename);
    // 文件的路径(不包含文件名称)
    console.log(__dirname);
    
    // 定时函数
    const a = setTimeout(()=>{
        console.log(123);
    },3000);
    
    setTimeout(()=>{console.log(123)},200);
    clearTimeout(a);
    
    // process.argv 是一个数组,默认情况前两项数据分别是:Node.js的安装环境的路径,当前执行的js文件的全路径
    // 从第三个参数开始表示命令行参数
    console.log(process.argv);
    console.log(process.argv0);
    
    // 打印当前系统的架构(x64, x86)
    console.log(process.arch);
    

    初识模块化(1-2)

    /*
    	index.js
        模块化开发
    
        传统非模块化开发有如下的缺点:
        1、命名冲突(团队开发)
        2、文件依赖(文件引入与先后次序)
    
        前端标准的模块化规范:
        1、AMD - requirejs
        2、CMD - seajs
    
        后端标准的模块化规范:
        1、CommonJS - Node.js
    
        模块化相关的规则:
        1、 如何定义模块:一个js文件就是一个模块,模块内部的成员都是相互独立的
        2、 模块成员的导出和引入
    */
    
    const a = function(a,b){
        return parseInt(a) + parseInt(b);
    }
    
    // 导出模块成员
    //exports.sum = a;
    
    module.exports = a; //此时这个模块本身就是个方法
    
    /*
    	a.js
        引入模块
    */
    
    const mod = require('./index.js');
    
    console.log(mod);
    
    //var ret = mod.sum(12,13);
    
    var ret = mod(12,13);
    
    console.log(ret);
    

    模块化细节补充

    /*
    	index.js
        模块成员的导出 global
    
        已经加载的模块会缓存,如果内存中有同样的文件就不会加载(性能优化)
    */
    
    console.log('hello');
    
    var flag = 123;
    
    // 很少使用
    global.flag = flag;
    
    /*
    	a.js
        模块文件的后缀3种情况: .js .json .node(C、C++)(不加后缀时加载顺序)
        */
    
    //require('./index.js'); 
    //require('./index.js');
    //require('./index.js');
    
    require('./index');   // 后缀可以直接省略
    
    var data = require('./data')
    
    console.log(global.flag);
    console.log(data);
    
    // data.json
    {
        "username":"张三",
        "age":12
    }
    

    ES6 let、const 使用规范

    /*
        声明变量 let和const
    */
    
    // let 声明的变量不存在预解析
    //console.log(flag);
    // var flag = 123;
    // let flag = 456;
    
    // let 声明的变量不允许重复(同一作用域)
    //let flag = 123;
    //let flag = 456;
    //console.log(flag);
    
    // ES6 引入了块级作用域
    // 块内部let定义的变量,在外部访问不到
    // if(true){
    //     //var flag = 123;
    //     let flag = 123;
    // }
    /* -------------------------------------------------------------------------- */
    {
        // 这里是块级作用域
        let flag = 123;
        console.log(flag);
    }
    //console.log(flag);
    /* -------------------------------------------------------------------------- */
    for(let i = 0; i < 3; i++){
        // for循环括号中let声明的变量只能在循环体中使用
        console.log(i);
    }
    /* -------------------------------------------------------------------------- */
    // 在块级作用域内部,变量只能先声明再使用,因为没有预解析
    if(true){
        //console.log(flag);
        let flag = 123;
    }
    
    /* ---------------------------------- const --------------------------------- */
    
    // const用来声明常量,不允许重新赋值,一开始必须要初始化
    // 上述规则对 const 同样适用
    // const n = 1;
    // n = 2;
    
    

    ES6 变量解构赋值

    /*
        变量的解构赋值
    */
    
    // var a = 1;
    // var b = 2;
    // var c = 3;
    
    // var a = 1,b = 2,c = 3;
    
    // * 数组的解构赋值,根据顺序,还可以添加默认值
    //let [a,b,c] = [1,2,3];
    
    //let [a=111,b,c] = [,123,];
    
    //console.log(a,b,c);
    
    // * 对象的解构赋值
    //let {foo,bar} = {foo:'hello',bar : 'hi'};
    //let {foo,bar} = {bar : 'hi',foo:'hello'};
    
    // * 对象属性别名(如果起了别名,那么原来的名字就无效了)
    // let {foo:abc,bar} = {foo:'hello',bar : 'hi'}
    
    // console.log(typeof(abc),bar,abc);
    
    // * 对象的解构赋值设置默认值
    let {foo:abc="hello",bar} = {bar : 'hi'};
    console.log(abc,bar);
    
    // * 把对象当中的属性与名称的进行绑定
    let {cos,sin,random} = Math;
    
    console.log(typeof cos,typeof sin,typeof random);
    /* -------------------------------------------------------------------------- */
    // * 字符串的解构赋值
    
    let [a,b,c,d,e,length] = "hello";
    console.log(a,b,c,d,e,length);
    
    let {length:len} = "hel";
    
    console.log(len);
    

    ES6 字符串扩展与模板字符串

    /*
        字符串相关扩展
        includes()  判断字符串中是否包含指定的子串(有的话返回true,否则返回false)
                    参数一:匹配的子串;参数二:从第几个字符开始匹配
        startsWith()  判断字符串是否以特定的子串开始
        endsWith()  判断字符串是否以特定的子串结束
    
        模板字符串
    */
    
    console.log('hello world'.includes('world',6));
    
    let url = 'admin/index.php';
    
    console.log(url.startsWith('admin'));
    console.log(url.endsWith('php'));
    
    /* -------------------------------------------------------------------------- */
    let obj = {
        username : '李四',
        age:'12',
        gender: 'male'
    };
    
    let tag = '
    '+obj.username+''+obj.age+''+obj.gender+'
    '
    ; // 反引号表示模板,模板中的内容可以有格式,通过 ${}方式填充数据 let fn = (x)=> x; let tag2 = `
    ${obj.username} ${obj.age} ${obj.gender} ${1+1} ${fn('nihao')}
    `
    console.log(tag); console.log(tag2); // * 模板字符串后期维护方便

    ES6 函数扩展

    /*
        函数扩展
        - 参数默认值
        - 参数解构赋值
        - rest参数
        - ...扩展运算符
    */
    
    //参数默认值老式方法
    function foo(param) {
        let p = param || 'hello';
        console.log(p);
    }
    foo();
    
    function foo2(param = 'nihao') {
        console.log(param);
    }
    
    foo2();
    foo2('123456');
    
    /* -------------------------------------------------------------------------- */
    
    function foo3(uname = 'list', age = 12) {
        console.log(uname, age);
    }
    
    foo3();
    foo3('张三', 13);
    
    // * 参数解构赋值
    function foo4({
        uname = 'lisi',
        age = 10
    } = {}) {
        console.log(uname, age);
    }
    
    foo4(); //! 不传参会出问题,可以给函数参数一个默认值 {}
    foo4({
        uname: 'zhangsan',
        age: 15
    });
    
    /* -------------------------------------------------------------------------- */
    // * rest参数(剩余参数)...
    function foo5(a, ...params) {
        console.log(params);
    }
    
    foo5(1, 2, 3, 4, 5);
    
    // 扩展运算符 ...
    
    function foo6(a, b, c, d, e, f){
        console.log(a + b + c + d + e + f);
    }
    
    foo6(1,2,3,4,5,6);
    let arr = [1,2,3,4,5,6];
    //foo6.apply(null,arr); 
    
    foo6(...arr);  // ! 扩展运算符
    
    // 合并数组
    let arr1 = [1,2,3];
    let arr2 = [4,5,6];
    let arr3 = [...arr1,...arr2];
    console.log(arr3);
    

    apply、call函数妙用

    ES6 箭头函数

    node执行调试完成之后进程退出,这时候去看它的打印内容就看不到了
    
    需要看到他具体的值,在打印的地方加上断点即可
    
    /*
        箭头函数
    */
    
    function foo(){
        console.log('hello');
    }
    foo();
    
    let foo2= () => console.log('hello');
    foo2();
    
    let foo3= x => x;
    foo3(123);
    
    // * 多个参数必须用小括号包住
    let foo4 = (a,b) => console.log(a+b);
    foo4(1,2);
    
    let arr = [1,2,3];
    arr.forEach(function(value,index){
        console.log(value,index);
    });
    
    arr.forEach((value,index)=>console.log(value,index));
    
    // ! 箭头函数的注意事项
    // - 箭头函数中的this取决于函数的定义位置而不是调用位置
    // - 箭头函数不可以 new
    // - 箭头函数不可以使用 arguments 获取参数列表,可以使用rest参数代替
    function foo5(){
        // 使用call调用foo5时,这里的this其实就是call的第一个参数
        console.log(this);
        setTimeout(()=>{
            console.log(this,this.num);
        },1000);
    }
    
    foo5();
    foo5.call({num:1});
    /* -------------------------------------------------------------------------- */
    let foo6 = ()=> {this.num = 123;}
    //new foo6();
    /* -------------------------------------------------------------------------- */
    let foo7 = (a,b) =>{
        console.log(a,b,arguments); //这种方式获取不到实参列表
    }
    foo7(123,456);
    
    let foo8 = (...param) => {  //rest参数
        console.log(param);
    }
    foo8(123,456);
    

    ES6 类与继承

    /*
        类与继承
    */
    // function Animal(name){
    //     this.name = name;
    // }
    
    // Animal.prototype.showName = function(){
    //     console.log(this.name);
    // }
    // var a = new Animal('Tom');
    // a.showName();
    // var b = new Animal('Jerry');
    // a.showName();
    /* -------------------------------------------------------------------------- */
    
    class Animal{
        // * 静态方法(静态方法只能通过类名调用,不可以使用实例对象调用)
        static showInfo(){
            console.log('hello');
        }
        // * 构造函数
        constructor(name){
            this.name = name;
        }
        showName(){
            console.log(this.name);
        }
    }
    let a = new Animal('spike');
    a.showName();
    Animal.showInfo();
    /* -------------------------------------------------------------------------- */
    // 类的继承 extends
    class Dog extends Animal{
        constructor(name,color){
            super(name); // super用来调用父类
            this.color = color;
        }
        ShowColor(){
            console.log(this.color);
        }
    }
    
    let d = new Dog('doudou','yellow');
    d.showName();
    d.ShowColor();
    Dog.showInfo();
    
    

    Buffer基本操作

    console.log(Buffer.isEncoding('utf8'));
    console.log(Buffer.isEncoding('gbk'));
    
    let buf = Buffer.from('hello');
    console.log(buf);
    console.log(Buffer.isBuffer(buf));
    console.log(Buffer.isBuffer({}));
    
    let buf2 = Buffer.from('中国','ascii');
    console.log(Buffer.byteLength(buf2));
    console.log(buf2.toString());
    
    let buf31 = Buffer.alloc(3);
    let buf32 = Buffer.alloc(5);
    let buf33 = Buffer.concat([buf31,buf32]);
    console.log(Buffer.byteLength(buf33));
    
    /* -------------------------------------------------------------------------- */
    // 实例方法 
    
    let buf4 = Buffer.alloc(5);
    buf4.write('hello',2,2); // * 向buffer对象中写入内容  [from] - counts
    console.log(buf4);
    
    let buf51 = Buffer.from('hello');
    let buf52 = buf51.slice();
    console.log(buf51==buf52); // ! 两个不同的buffer对象
    buf52 = buf51.slice(1,3); // * 截取buffer对象的内容 [from] - (to)
    console.log(buf52.toString());
    
    // ! toJSON方法不需要显式调用,当JSON.stringify方法调用的时候会自动调用toJSON方法 
    const buf6 = Buffer.from('hello');
    const json = JSON.stringify(buf6); // * 将buffer对象转成JSON,对应的十进制
    console.log(json);
    

    Node.JS 路径操作

    /*
        路径操作
    */
    const path = require('path');
    
    // * 获取路径的最后一部分
    console.log(path.basename('/foo/bar/baz/asdf/quux.html'));
    console.log(path.basename('/foo/bar/baz/asdf/quux.html','.html'));
    
    // * 获取路径
    console.log(__dirname);
    console.log(path.dirname('/abc/qq/www/aabc.txt'));
    
    // * 获取文件扩展名
    console.log(path.extname('index.html'));
    
    // * 路径的格式化处理
    // path.format() obj->string
    // path.parse() string->obj
    
    let obj = path.parse(__filename);
    console.log(obj);
    
    /*
    {
      root: 'F:\\',   文件根路径
      dir: 'F:\\Node.JS\\Codes\\1031\\NodeJS基础\\2-1 路径操作', 文件全路径
      base: 'index.js', 文件名称
      ext: '.js',   文件扩展名
      name: 'index' 文件名称
    }
    */
    
    let objpath = {
        root: 'd:\\',  
        base: 'abc.txt',
        dir : 'd:\\aaaaa\\cccc\\', 
        ext: '.txt',   
        name: 'abc', 
    };
    let strPath = path.format(objpath);
    console.log(strPath);
    
    // * 判断是否为绝对路径
    path.isAbsolute('C:/foo/..'); 
    
    // * 拼接路径,在连接路径的时候会格式化
    console.log(path.join('/foo','bar','baz/asdf','quux','../../')); // ! 两个点是上层路径,一个点表示当前路径
    
    // * 规范化路径
    console.log(path.normalize('/foo/bar//baz/asdf/quux/..'))
    console.log(path.normalize('C:\\temp\\\\foo\\bar\\..\\'));
    
    // * 从两个绝对路径换算出来相对路径
    console.log(path.relative('C:\\orandea\\test\\aaa', 'C:\\orandea\\impl\\bbb'));
    
    // * 解析路径
    console.log(path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif'));
    
    // * 两个特殊属性
    console.log(path.delimiter); // * 环境变量分隔符 windows 是 ; linux 是 :
    console.log(path.sep);  // * 路径分隔符,windows 是 \  LINUX是 /
    

    异步I/O

    /*
        异步I/O input/output
        - 文件操作
        - 网络操作
    
        在浏览器中也存在异步操作:
        - 定时任务
        - 事件处理
        - Ajax回调处理
    
        js的运行是单线程的
        引入了事件队列机制
    
        Node.js中的事件模型与浏览器中的事件模型类似
        单线程+事件队列
    
        Node.js中异步执行的任务:
        1、文件I/O
        2、网络I/O
    
        基于回调函数的编码风格
    */
    

    文件操作

    文件状态

    /*
        文件状态
    */
    const fs = require('fs');
    console.log(1);
    fs.stat(`${__dirname}/data.txt`,(err,stat)=>{
        // ! 一般回调函数的第一个参数是错误对象,如果err为null,表示没有错误,否则表示报错了
        if(err) return;
        console.log(stat);
        if(stat.isFile()){
            console.log('文件');
        }
        else if(stat.isDirectory()){
            console.log('目录');
        }
        /* 
        atime 访问时间
        mtime 文件数据发生变化的时间
        ctime 文件状态信息发生变换的时间(比如文件的权限)
        birthtime 文件创建的时间
        */
    });
    console.log(2);
    
    // 同步操作
    // console.log(1);
    // let ret = fs.statSync(`${__dirname}/data.txt`);
    // console.log(ret);
    // console.log(2);
    
    // ! 当主线程执行完了才会空闲,去把事件队列中的任务取出来
    

    读文件

    /*
        读文件操作
    */
    const fs = require('fs');
    const path = require('path');
    
    let strPath = path.join(__dirname,'data.txt');
    
    fs.readFile(strPath,(err,data)=>{
        if(err) return;
        console.log(data); // * 打印出字节数组
        console.log(data.toString());
    });
    
    // * 第二个参数可以指定编码,得到的数据是字符串
    // * 如果没有第二个参数,那么得到的就是Buffer实例对象
    fs.readFile(strPath,'utf8',(err,data)=>{
        if(err) return;
        console.log(data); // * 打印出字符串
    });
    
    // ! 同步操作,不需要回调函数
    let ret = fs.readFileSync(strPath,'utf8');
    console.log(ret);
    

    写文件

    /*
        写文件操作
    */
    const fs = require('fs');
    const path = require('path');
    
    let strpath = path.join(__dirname,'data.txt');
    // * 默认编码方式 encoding : utf 8
    // fs.writeFile(strpath,'hello cat',(err)=>{
    //     if(err) throw err;
    //     else {
    //         console.log('文件写入成功');
    //     }
    // })
    // ! 多次写入需要使用数据流的方式
    
    // let buf = Buffer.from('world');
    // fs.writeFile(strpath,buf,(err)=>{
    //     if(err) throw err;
    //     else {
    //         console.log('文件写入成功');
    //     }
    // })
    
    // ! 同步操作
    fs.writeFileSync(strpath,'tom and jerry');
    

    文件流式操作(针对大文件)

    /*
        大文件操作(流式操作)
        fs.createWriteStream(path[, options])
        fs.createReadStream(path[, options])
    */
    const path = require('path');
    const fs = require('fs');
    
    let sPath = path.join(__dirname,'../files','file.zip');
    let dPath = path.join('P:\\','file.zip');
    
    let readStream = fs.createReadStream(sPath);
    let writeStream = fs.createWriteStream(dPath);
    
    // 基于事件的处理方式
    // * 举例
    // $('input[type=button]').on('click',function(){
    //     console.log('hello');
    // });
    
    // * 在NodeJS中无DOM操作,所以没有点击事件
    
    // let num = 0;
    // readStream.on('data',(chunk)=>{ // ! data 是固定的事件,每读取一部分就触发
    //     num++;
    //     writeStream.write(chunk);
    // });
    
    // readStream.on('end',()=>{ // ! end 是固定的事件
    //     console.log(num);
    //     console.log('文件处理完成');
    // })
    
    /* -------------------------------------------------------------------------- */
    
    // * 另外一种方式,pipe的作用直接把输入流(从磁盘加载到内存)与输出流(从内存写入到磁盘)
    readStream.pipe(writeStream);
    
    /* -------------------------------------------------------------------------- */
    
    fs.createReadStream(sPath).pipe(fs.createWriteStream(dPath));
    

    目录操作

    /*
        目录操作
        - 创建目录
        fs.mkdir(path[, options], callback)
        fs.mkdirSync(path[, options])
        - 读取目录
        fs.readdir
        fs.readdirSync
    
    */
    
    const path = require('path');
    const fs = require('fs');
    
    // fs.mkdir(path.join(__dirname,'../files','abc'),(err)=>{
    //     console.log(err);
    // });
    
    // fs.mkdirSync(path.join(__dirname,'../files','abc'));
    /* -------------------------------------------------------------------------- */
    // 读取目录下的目录与文件
    // fs.readdir(path.join(__dirname,'..'),(err,files)=>{
    // console.log(err,files);
    //     files.forEach((item,index)=>{
    //         fs.stat(path.join(__dirname,'..',item),(err,stat)=>{
    //             if(stat.isFile()){
    //                 console.log(item,'文件');
    //             }
    //             else if(stat.isDirectory()){
    //                 console.log(item,'目录');
    //             }
    //         })
    //     })
    // });
    
    // ! 同步操作
    // let files = fs.readdirSync(path.join(__dirname,'..'));
    // files.forEach((item,index)=>{
    //     fs.stat(path.join(__dirname,'..',item),(err,stat)=>{
    //         if(stat.isFile()){
    //             console.log(item,'文件');
    //         }
    //         else if(stat.isDirectory()){
    //             console.log(item,'目录');
    //         }
    //     })
    // })
    
    // ! 删除目录
    fs.rmdir(path.join(__dirname,'../files','abc'),(err)=>{
        console.log(err);
    });
    
    fs.rmdirSync(path.join(__dirname,'../files','abc'));
    

    文件实操案例(初始化目录与文件)

    /*
        文件操作案例(初始化目录结构)
    */
    
    const path = require('path');
    const fs = require('fs');
    
    let root = 'P:\\'
    let fileContent = `
    
    
    
        
        
        
        Document
    
    
        欢迎访问
    
    
    `;
    
    // 初始化数据
    let initData = {
        projectName : 'mydemo',
        data : [{
            name : 'img',
            type : 'dir'
        },{
            name : 'js',
            type : 'dir'
        },{
            name : 'css',
            type : 'dir'
        },{
            name : 'index.html',
            type : 'file'
        }]
    };
    
    // 创建项目根路径
    fs.mkdir(path.join(root,initData.projectName),(err)=>{
        if(err) return;
        // 创建子目录和文件
        initData.data.forEach((item)=>{
            if(item.type == 'dir'){
                // * 创建子目录
                fs.mkdirSync(path.join(root,initData.projectName,item.name));
            }
            else if(item.type == 'file'){
                fs.writeFileSync(path.join(root,initData.projectName,item.name),fileContent);
            }
        })
    });
    
    

    __EOF__

  • 本文作者: 小能正在往前冲
  • 本文链接: https://www.cnblogs.com/linxiaoxu/p/16702618.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    全面解析UDP协议(特点、报文格式、UDP和TCP的区别)
    深入理解Java并发锁
    MATLAB使用速成 第七章(多项式运算与代数方程求解)
    maya显示隐藏 动画长度
    2022年数学建模国赛--赛后总结
    在ubuntu上用QT写一个简单的C++小游戏(附源码)
    HTML5中表单提交的几种验证方法
    从matlab的bwmorph函数的'majority'参数中扩展的一种二值图像边缘光滑的实时算法。
    Java版工程行业管理系统源码-专业的工程管理软件- 工程项目各模块及其功能点清单
    情绪智力测试
  • 原文地址:https://www.cnblogs.com/linxiaoxu/p/16702618.html