• js逆向学习笔记【一篇就够】


    js逆向学习笔记【一篇就够】

    • 不够再来一篇

    文章目录

    算法还原

    白盒还原

    • 直接扣算法,或者是标准算法
    • 理解对方 js 的意思,能够翻译成其他语言,可能会占用较长的分析时间

    黑盒还原

    • 直接整体调用(加密复杂,更新频繁,整体关联度高)
    • 不需要关注算法逻辑,需要模拟浏览器环境,需要对抗环境检测

    RPC 调用

    • 算法复杂度高,浏览器环境难以模拟
    • 找到算法位置,暴露出来,直接 rpc 调用,需要保证浏览器状态(内存泄漏,保活)

    浏览器自动化

    • 无法逆向
    • 接近真人,但是有大量的自动化痕迹;

    基本数据类型

    • 数值 Number:整数和小数
    • 字符串 String:文本
    • 布尔值 Boolean:布尔值,true 表示真,false 表示假
    • undefined:未定义,或者不存在
    • null:表示空值
    • 对象 Object:各种值组成的集合

    原始类型

    1. 数值
    2. 字符串
    3. 布尔值
    • 原始类型就是最基本的数据类型,不能再进行细分;
    • undefined 和 null 一般看成是两个特殊值;

    合成类型

    • 对象
      • 狭义的对象 Object
      • 数组 Array
      • 函数 Function
    • 一个对象往往是由多个类型的值组成,可以看成是一个存放各种值的容器

    查看类型

    • typeof:返回一个值的数据类型

    • instanceof:表示对象是否是某个构造函数的实例

    • Object.prototype.toString

    • typeof 可以用来检查一个未声明的变量,而不报错;

      // 基本数据类型
      var tmp1 = "字符串";
      var tmp2 = 1;
      var tmp3 = 1.1;
      var tmp4 = true;
      
      // 特殊类型
      var tmp5=  undefined;
      var tmp6 = null; // null 是一个 object
      
      // 对象
      var tmp7 = {}; // 对象
      var tmp8 = []; // 数组
      var tmp9 = function(){}; // 函数
      
      console.log("typeof(tmp1)", typeof (tmp1));
      console.log("typeof(tmp2)", typeof (tmp2));
      console.log("typeof(tmp3)", typeof (tmp3));
      console.log("typeof(tmp4)", typeof (tmp4));
      console.log("typeof(tmp5)", typeof (tmp5));
      console.log("typeof(tmp6)", typeof (tmp6));
      console.log("typeof(tmp7)", typeof (tmp7));
      console.log("typeof(tmp8)", typeof (tmp8));
      console.log("typeof(tmp9)", typeof (tmp9));
      
      // function 因为有类object 的操作, 所以也属于 object
      console.log("tmp9.name", tmp9.name); // 获取函数名
      
      //typeof(tmp1) string
      //typeof(tmp2) number
      //typeof(tmp3) number
      //typeof(tmp4) boolean
      //typeof(tmp5) undefined
      //typeof(tmp6) object
      //typeof(tmp7) object
      //typeof(tmp8) object
      //typeof(tmp9) function
      //tmp9.name tmp9
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38

    null undefined 和布尔值

    null 和 undefined 的区别

    • null 表示一个空对象,undefined 表示未定义;
    • null 转为数值的时候为 0, undefined 转为数值的实收为 NaN;

    boolean

    布尔值表示真和假,true 表示真,false 表示假;

    下列运算符会返回布尔值:

    1. ! (not)
    2. 相等运算符: =, , !, !=
    3. 比较运算符: >=, <=, <,>

    表示 false 的值

    在自动数据转换中,下列值会表示 false:

    1. undefined
    2. null
    3. false
    4. 0
    5. NaN
    6. “” 或者 ‘’ 空字符串

    其他的值都会被当成 true;

    空数组 [] 和空对象 {} 对应的布尔值都是 true;

    数值

    在 js 中,所有的数值都是 64 位浮点数的形式进行存储的,也就是说在 js 底层,没有整数只有浮点数;

    因为浮点数精度的问题,js 在进行浮点数运算的时候经常会出现问题:

    console.log(0.1 + 0.2);
    // 0.30000000000000004
    
    • 1
    • 2

    进制

    • 十进制:没有前导,直接用数值表示
    • 八进制:有前缀 0o 或者 0O
    • 十六进制:有前缀 0x 或者 0X
    • 二进制:有前缀 0b 或者 0B

    默认情况下,js 内部会将八进制,十六进制,二进制转为十进制;

    NaN

    NaN 是 js 中的特殊值,表示非数字 (Not a Number), 主要出现在字符串解析成数字出错的时候;

    1. NaN 不是独立的数据类型,它是一个特殊值,它的数据类型依然是 Number
    2. NaN 不等于任何值,包括它本身 (不等于本身可以用来检测某个值是否是 NaN)
    3. NaN 和任何数运算,得到的结果都是 NaN;

    Infinity

    Infinity 用来表示无穷,一般出现在两种场景下:

    1. 正数的数值太大,或者负数的数值太小;
    2. 非 0 的数除以 0, 得到 Infinity
    • js 中数值正向溢出或者负向溢出或者被 0 除都不会报错,所以单纯的数学运算几乎没有可能抛出异常
    • Infinity 大于一切数值 (除了 NaN); -Infinity 小于一切数值 (除了 NaN)
    • Infinity 和 NaN 比较,总是返回 false;

    全局 api

    parseInt(string[,radix])

    parseInt(string[,radix]) 将字符串解析成数值;如果入参非字符串,则会调用入参的 toString 方法转换为字符串再进行转换;如果设置了第二个参数 radix, 则会将字符串按指定的 radix 进制转换成十进制;返回数值或者 NaN

    var a = '0xf';
    console.log(a, 16); // 将 16 进制的 0xf 转为十进制
    // 15
    
    • 1
    • 2
    • 3

    parseFloat(string)

    parseFloat(string) 将字符串入参解析成浮点数;返回浮点数或者 NaN ;

    var a = "4.567";
    console.log(parseFloat(a)); // 4.567
    
    // 当入参有非法字符, 则只保留合法部分进行转换
    var b = "4.567abcd";
    console.log(parseFloat(b)); // 4.567
    
    // 第二个小数点忽略
    var c = "1.2.3";
    console.log(parseFloat(c)); // 1.2
    
    // 起始为非法字符则直接返回 NaN
    var d = "aaa1.2"; // NaN
    console.log(parseFloat(d));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    isNaN()

    判断某个入参是否是 NaN; 可以利用 NaN 的不等性来进行判断

    var a = NaN;
    if (a != a ){
        console.log("it is NaN");
    }
    
    • 1
    • 2
    • 3
    • 4

    isFinite()

    判断某个入参是否是 Infinity;

    字符串

    • 字符串和数组相似,都支持使用 [] 运算符来通过指定索引来获取值;
    • length: 可以获取字符串的长度
    • 字符串不能通过 [] 运算符和索引来修改字符串的值

    字符集

    js 中使用的字符集为 Unicode 字符集,所有的字符串都使用 Unicode 表示;

    var f\u006F\u006F = 'abc';
    console.log(f\u006F\u006F);
    
    • 1
    • 2

    base64 转码

    • 浏览器:
      • btoa:任意值转为 base64 编码
      • atob:base64 值解码
      • 非 ASCII 码 (如中文) 要转码之后再 base64;
        • encodeURIComponent
        • decodeURIComponent
    • Nodejs
      • var base64encode = Buffer.from("js").toString("base64");
      • var base64decode = Buffer.from(base64encode,'base64').toString();

    对象

    • 对象就是一组键值对 key-value 的集合,是一种无序的复合数据集合
    • 对象的每个键名又称为属性 property; 它的值可以是任意数据类型;
    • 如果一个对象的某个属性的值是函数,则通常将这个属性称为该对象的方法,可以像函数一样调用这个方法;
    • 属性可以动态创建,不必在对象声明的时候就全部定义;

    对象引用

    • 如果不同的变量名指向同一个对象,那么他们都是对这个对象的引用;也就是说这些变量都指向了同一个内存地址,修改其中任意一个的值都会影响到其他的变量;
    • 如果取消某一个变量对于原对象的引用,不会影响到其他引用该对象的变量
    • 这种引用只局限于对象,如果两个变量指向同一个原始类型的值,这些变量都是对原始类型的值的拷贝,改变任意变量都不会影响其他变量

    属性查看

    Obejct.keys() 可以查看该对象自身的属性,继承来的属性无法查看

    var a = {
        hello: function(){
            consoel.log("hi");
        },
        table: [1, 2, 3],
        name: "kevin",
        age: 21,
        married: false
    }
    
    console.log(Object.keys(a));
    //[ 'hello', 'table', 'name', 'age', 'married' ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    属性删除

    • delete 用于删除对象的属性,删除成功后返回 true
    • 删除一个不存在的属性,delete 不会报错,而且返回 true
    • 只有一种情况,delete 命令会返回 false; 那就是删除一条存在的属性,但是这条属性被定义成不能删除;(定义该属性不能删除 defineProperty)
    • 只能删除对象自身的属性,继承来的属性不能删除
    var a = {
        hello: function(){
            consoel.log("hi");
        },
        table: [1, 2, 3],
        name: "kevin",
        age: 21,
        married: false
    }
    
    delete a.age, delete a.hello;
    console.log(Object.keys(a));
    // [ 'table', 'name', 'married' ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    属性存在判断

    • in 运算符可以用于检查对象是否包含某个属性;检查的是键名,存在这个属性返回 true, 不存在则返回 false;
    • hasOwnProperty(): 判断某个属性是否是该对象自身的属性;
    JSvar a = {
        hello: function () {
            consoel.log("hi");
        },
        table: [1, 2, 3],
        name: "kevin",
        age: 21,
        married: false
    }
    console.log(a.hasOwnProperty('table'));
    if ("table" in a) {
        console.log("table is property of a");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    属性遍历

    • for in 循环可以用于遍历对象的全部属性;不仅可以遍历对象自身的属性,还可以遍历对象继承来的属性;
    • 如果只想遍历对象自身的属性,可以配合 hasOwnProperty() 进行筛选
    var a = {
        hello: function(){
            consoel.log("hi");
        },
        table: [1, 2, 3],
        name: "kevin",
        age: 21,
        married: false
    }
    
    for (const aKey in a) {
        // 使用 hasOwnProperty 进行筛选
        if (a.hasOwnProperty(aKey)) {
            console.log(aKey);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    函数

    函数声明

    js 中函数声明有三种方式

    1. 使用 function 申明 function a(){}
    2. 函数表达式 var a = function(){}
    3. Function 构造函数 var a = Function("a","b", "return a+b") 或者 var a = new Function("a","b", "return a+b") 这两种方式效果一样

    函数是一等公民

    js 将函数看成是一个值,与其他数据类型一样,凡是可以使用其他数据类型的地方都可以使用函数,例如:

    1. 可以将函数赋值给变量或者对象的属性
    2. 可以将函数作为参数传递给其他函数
    3. 可以将函数作为其他函数的返回值

    函数变量名提升

    js 中全局变量名存在变量提升,函数内部的局部变量也存在变量提升;

    function outer() {
        console.log(a); // undefined 说明全局变量存在变量提升
        console.log(b); // undefined 说明局部变量存在变量提升
        var b = 2;
    }
    outer();
    var a = 1;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    js 中函数的声明也存在变量提升,可以先调用该方法,再定义该方法

    b();
    function b() {
        console.log("b called");
    }
    
    • 1
    • 2
    • 3
    • 4

    函数的属性和方法

    • name (属性):返回函数的名字
    • length (属性):返回函数预期传入的形参数量
    • toString () 方法:返回函数的字符串源码

    函数作用域

    • 作用域 scope 是指变量存在的范围
      • es5 中 js 只有两个作用域
        • 全局作用域:变量在整个程序中一直存在,所有地方都可以读取到该变量
        • 函数作用域:变量只在函数内部存在
      • es6 中新增了块级作用域
    • 函数外部声明的变量就是全局变量,它可以在函数内部读取到;
    • 在函数内部声明的变量就是局部变量,函数外部无法读取;
    • 函数本身的作用域就是其声明时所在的作用域,与其运行时的作用域无关;
    • 函数内部声明的函数,作用域绑定函数内部 (闭包)

    函数参数省略

    js 中函数的参数不是必须的,允许省略函数的参数;

    函数的 length 属性只和函数声明时形参的个数有关,和实际调用时传入的参数个数无关;

    参数传递方式

    • 函数的参数如果是原始数据类型 (数值,字符串,布尔), 参数传递使用按值传递的方式,在函数内部修改参数的值不会影响函数外部
    • 如果函数的参数是复合数据类型 (数组,对象,其他函数), 参数的传递方式是按址传递,传入的是引用的地址,因此在函数内部修改参数,会影响到原始值;

    arguments 对象

    • 因为 js 允许函数有不定数目的参数,所以需要在函数体的内部可以读取到所有参数,这就是 arguments 对象的由来
    • arguments 对象包含了函数运行时的所有参数,这是一个类数组对象,arguments[0] 就是第一个参数;
    • arguments 对象只能在函数内部使用
    • arguments.length 可以获取函数调用时入参的真正个数
    • arguments.callee 属性可以获取对应的原函数
    • arguments 对象是一个类数组对象,如果要让他使用真正的数组方法,需要将 arguments 转换成数组:
      • Array.prototype.slice.call(arguments)
      • 新建数组,遍历 arguments 将元素 push 到新数组中;

    闭包

    • 要理解闭包首先要理解 js 的作用域;前面提到的 js 在 es5 中只有两种作用域:
      • 全局作用域
      • 函数作用域
    • 在函数的内部可以全局作用域的变量
    • js 中特有的链式作用域结构,子级会向上一级一级寻找所有父级的变量,父级的所有变量对于子级来说都是可见的,反之不成立;
    var a = 1;
    var b = 2;
    function f() {
        var b = 3;
        console.log(a, b);
        function f1() {
            var b = 4;
            console.log(a, b);
        }
        f1();
    }
    f(); // 1 3// 1 4
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    链式作用域查找

    子级会优先使用自己的作用域,如果变量存在则使用,不存在则会依次向上寻找,直至全局作用域;

    闭包定义

    • 闭包可以简单理解成定义在一个函数内部的函数
    • 闭包最大的特点就是它可以记住自己诞生的环境,本质上,闭包就是将函数内部和函数外部连接起来的桥梁;
    • 闭包最大的用处有两个
      1. 可以直接读取到外层函数内部的变量
      2. 可以让这些变量始终保存在内存中,闭包让自己诞生的环境一直存在

    通过闭包实现简单的计数器

    function count() {
        var count = 0;
        function f() {
            count++;
            console.log("count", count);
        }
        return f;
    }
    f = count();
    f();
    f();
    f();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    立即调用函数表达式

    js 中有三种立即调用函数的方式

    1. var f = function(){}();
    2. (function(){}())
    3. (function(){})()

    通常情况下,只对匿名函数使用这种立即执行的表达式,这样有两个目的:

    1. 不必为函数命名,避免污染全局环境
    2. 立即调用函数的内部会形成单独的作用域,可以封装一些外部无法读取的私有变量

    eval 命令

    • eval 可以接受一个字符串,并将字符串当做代码执行
    • eval 没有自己的作用域,都是使用当前运行的作用域,所以 eval 会修改当前作用域下的变量的值
    • eval 的本质是在当前作用域中,注入代码,经常用于混淆和反爬

    eval 别名调用

    eval 的别名调用在 nodejs 下无法跑通,需要在浏览器下运行;

    需要注意,在 eval 通过别名调用的时候,作用域永远是全局作用域;

    var a = 1;
    var e = eval;
    (function () {
        var a = 2;
        e("console.log(a);"); // eval 在别名调用的时候使用全局作用域
    }())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    数组

    • 数组 Array 是按次序排列的一组值
    • 每个值的位置都有对应的索引
    • 数组使用 来表示
    • 任何类型的数据都可以放入数组中
    • 本质上,数组是特殊的对象,typeof 查看数组的类型返回的是 object
    • Object.keys () 可以返回数组的键名 (索引)

    数组的属性

    • length: 表示数组的元素个数,这个属性是可写的,可以直接修改数组的 length 属性,来实现清空数组或删除数组中元素的效果
    • 数组本质上是特殊的对象,支持使用点操作符对数组添加属性;
    var a = 1;
    var e = eval;
    (function () {
        var a = 2;
        e("console.log(a);"); // eval 在别名调用的时候使用全局作用域
    }
        ())
    var a = [1, 1.1, true, {}, [], "hello", null, undefined];
    console.log("a.length", a.length);
    a.name = "add a.name property";
    for (const aKey in a) {
        console.log(aKey, a[aKey]);
    }
    console.log("Object.keys(a)", Object.keys(a));
    a.length = 0;
    console.log("a", a);
    console.log("a['name']", a['name']);
    console.log("a.name", a.name);
    console.log("a[0]", a[0]);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    数组循环

    • for in 循环
    • for 循环
    • while 循环
    • forEach : 只有数组才有该方法;该方法接受一个回调函数,回调函数入参为 value 和 key;
    var a = 1;
    var e = eval;
    (function () {
        var a = 2;
        e("console.log(a);"); // eval 在别名调用的时候使用全局作用域
    }
        ())
    var a = [1, 1.1, true, {}, [], "hello", null, undefined];
    console.log("a.length", a.length);
    a.name = "add a.name property";
    for (const aKey in a) {
        console.log(aKey, a[aKey]);
    }
    console.log("Object.keys(a)", Object.keys(a));
    a.length = 0;
    console.log("a", a);
    console.log("a['name']", a['name']);
    console.log("a.name", a.name);
    console.log("a[0]", a[0]);
    var a = [1, 1.1, true, {}, [], "hello", null, undefined];
    
    for infor(const aKey in a) {
        console.log("aKey:", aKey, "value:", a[aKey]);
    }
    
    console.log("-------------------------------")
    
    forfor(var i = 0; i <= a.length; i++) {
        console.log("index:", i, "value:", a[i]);
    }
    
    console.log("-------------------------------")
    
    whilevar index = 0;
    while (index <= a.length) {
        console.log("index:", index, "value:", a[index]);
        index++;
    }
    
    console.log("-------------------------------")
    
    forEacha.forEach(function (value, key) {
        console.log("key:", key, "value:", value);
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    数组空值

    js 中的数组支持空值,出现空值时会占用该索引位,但是遍历的时候不会遍历该索引的值

    var a = [1, 2, 3, , 5];
    a.forEach(function (value, key) {
        console.log("key", key, "value", value);
    })
    // key 0 value 1
    // key 1 value 2
    // key 2 value 3
    // key 4 value 5
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    类数组对象

    • 如果一个对象的所有键名都是正整数或者 0, 且有 length 属性,那么这个对象就是类数组对象
    • 典型的类数组对象有 arguments 对象,字符串,以及大部分的 dom 元素集
    • 数组的 slice 方法可以将类似数组的对象变成真正的数组 var arr = Array.prototype.slice.call(arrayLike);
    • 除了将类数组对象转成真正的数组,还可以使用 call () 将数组的方法直接放到类数组对象上使用 Array.prototype.forEach.call(arrayLike, function(){});

    数据类型转换

    自动数据类型转换

    其他类型转字符串

    + 加号作为操作符,且操作数中含有字符串时,会自动将另一个操作数转为字符串;

    规则如下:

    1. 字符串 + 基础数据类型:会直接将基础数据类型转为和字面量相同的字符串
    2. 字符串 + 复合数据类型:复合数据类型会先调用 valueOf() 方法,如果该方法返回基础数据类型则将其转为字符串,如果返回的是复合数据类型,则调用 toString() 方法,如果返回的是基础数据类型则将其转为字符串,如果不是则报错;
    // 基础类型
    // 自动数据类型转换
    // + 字符串
    // 1. 字符串 + 基础数据类型: 会直接将基础数据类型转为和字面量相同的字符串
    var tmp1 = "" + 3;
    console.log("tmp1", tmp1); // tmp1 "3"
    
    var tmp2 = "" + true;
    console.log("tmp2", tmp2); // tmp2 "true"
    
    var tmp3 = "" + undefined;
    console.log("tmp3", tmp3); // tmp3 "undefined"
    
    var tmp4 = "" + null;
    console.log("tmp4", tmp4); // tmp4 "null"
    
    // 字符串+复合数据类型: 复合数据类型会先调用 valueOf 方法, 如果此方法返回的是引用类型, 则再调用 toString()方法, 最后将返回值转为字符串类型
    var tmp5 = [1, 2, 3] + "";
    console.log("tmp5", tmp5); // tmp5 1,2,3
    
    var tmp6 = {} + "";
    console.log("tmp6", tmp6); // tmp6 [object Object]
    
    // 重写 toString 方法
    var o = {
        toString: function () {
            return 1;
        }
    }
    
    var tmp7 = o + "";
    console.log("tmp7", tmp7) // tmp7 "1"
    
    // 重写 valueOf 方法
    o.valueOf = function () {
        return 2;
    }
    var tmp8 = "" + o;
    console.log("tmp8", tmp8); // tmp8 2
    
    var a = {
        valueOf: function () {
            return {}
        },
        toString: function () {
            return "toString"
        }
    };
    
    console.log("" + a); // toString
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    其他类型转布尔值

    数值转布尔值

    数值在逻辑判断条件下会自动转成布尔值;±0 和 NaN 为 false, 其他数值都是 true;

    if (0) {
        console.log("0 is true");
    }else{
        conso
    • 1
    • 2
    • 3
  • 相关阅读:
    STC51单片机27——控制无刷电机
    入选全球灯塔工厂 西部数据践行可持续发展承诺
    【MATLAB】 02 结构化程序与自定义函数
    [管理与领导-83]:IT基层管理者 - 核心技能 - 高效执行力 - 8- 提升执行力的三大方法:目标复述、任务分解、寻求帮助
    20天深度复习JavaSE的详细笔记(十二)——集合(Collection、数据结构、List、泛型深入)
    第2-3-7章 个人网盘服务接口开发-文件存储服务系统-nginx/fastDFS/minio/阿里云oss/七牛云oss
    1002 写出这个数【PAT (Basic Level) Practice (中文)】
    社区新零售:改变生活方式的创新商业模式
    低代码平台和无代码平台有什么区别
    Android 蓝牙HFP通话源码分析 ---在手机端拨打电话,SCO链路连接流程(三.2)
  • 原文地址:https://blog.csdn.net/weixin_38640052/article/details/127413429