zoukankan      html  css  js  c++  java
  • 前端笔记之JavaScript(九)定时器&JSON&同步异步/回调函数&函数节流&call/apply

    一、快捷位置和尺寸属性

    DOM已经提供给我们计算后的样式,但是还是觉得不方便,因为计算后的样式属性值都是字符串类型

    不能直接参与运算。

    所以DOM又提供了一些API:得到的就是number类型的数据,不需要parseInt(),直接可以参与运算。

     offsetLeftoffsetTop

     offsetWidthoffsetHeight

     clinetWidthclinetHeight


    1.1 offsetWidthoffsetHeight

    全线兼容,是自己的属性,和别的盒子无关的。

    一个盒子的offsetWidth值就是自己的width+左右padding+左右border的宽度

    一个盒子的offsetHeight值就是自己的height+上下padding+上下border的宽度

     var oBox = document.getElementById("box");

     alert(oBox.offsetWidth)

     alert(oBox.offsetHeight)

    如果盒子没有宽度,那么浏览器都将px值当做offsetWidth,而不是100%

    如果盒子没有高度,用内容撑开,那么浏览器都将px值当做offsetWidth


    1.2 clientWidthclientHeight

    全线兼容,就IE6有一点点问题。

     var oBox = document.getElementById("box");

     alert(oBox.clientWidth)

     alert(oBox.clientHeight)

    clientWidth就是自己的width+padding的值,也就是说,比offsetWidth少了border

    clientHeight就是自己的height+padding的值,也就是说,比offsetHeight少了border

    如果盒子没有宽度,那么浏览器都将px值当做clientWidth,而不是100%

    如果盒子没有高度,用内容撑开,IE6clientHeight0,其他浏览器都是合理数值。


    1.3 offsetLeftoffsetTop属性

    获取距离方法是一样,参考元素也是一样的,以offsetLeft举例。

    offsetLeft:偏移某个元素的左侧的距离。

    offsetTop:偏移某个元素的顶部的距离

    这两个属性兼容性非常差,不要急,慢慢看。

    IE9IE9+Chrome等高级浏览器中:

    一个元素的offsetLeft值,就是这个元素的左边框外,到自己offsetParent对象的左边框内的距离number类型)

    offsetParent是:自己祖先元素中,已经定位的元素,不用考虑自己是否定位。

     

    每个元素,天生都有一属性,叫“offsetParent”,表示自己的“偏移参考盒子”。offsetParent就是自己祖先元素中,离自己最近的已经定位的元素,如果自己祖先元素中,没有任何定位的盒子,那么offsetParent对象就是body

    <body class="body">
        <div class="box1">
            <div class="box2">
                <div class="box3">
                    <p></p>
                </div>
            </div>
        </div>
    </body>
    var op = document.getElementsByTagName('p')[0];
    alert(op.offsetLeft);
    alert(op.offsetParent.className);

    IE6IE7offsetParent对象是谁,和高级浏览器有非常大的不同:

    情形1:如果自己没有定位,那么自己的offsetParent对象就是自己的祖先元素中,离自己最近的有width或有height的元素

    <body class="body">
        <div class="box1"> → 有宽高,不是离的最近的
            <div class="box2">  → ,有宽高,offsetParent
                <div class="box3">  → ,没有宽高
                    <p></p> → 没有定位
                </div>
            </div>
        </div>
    </body>

    情形2:自己如果有定位属性,那么自己的offsetParent就是自己祖先元素中离自己最近的有定位的元素,如果父亲都没有定位就的HTML元素

    <body class="body">
        <div class="box1">
            <div class="box2"> → 有宽高,有定位,offsetParent
                <div class="box3"> → 有宽高,没有定位
                    <p></p> → 有定位,找定位的父亲,没定位就找有宽高的父亲
                </div>
            </div>
        </div>
    </body>

    IE8offsetParent是谁呢?和高级浏览器一致:

    无论自己是否定位,自己的offsetParent就是自己祖先元素中,离自己最近的已经定位的元素。

    这一点,没有任何兼容问题!但是,多算了一条父亲的边框

    总结:

    IE67

    IE8

    IE9IE9+、高级浏览器

    offsetParent

    如果自己没有定位,那么自己的父亲中有width或有height或者有定位的元素。

    如果自己有定位,那么就是和高级浏览器一致。

    和高级浏览器一致

    自己祖先元素中,离自己最近的已经定位的元素

    offsetLeft

    offsetTop

    和高级浏览器一致

    多算一条offsetParent(父亲)边框

    自己的边框外到offsetParent对象的边框内

    兼容性解决办法:不是能力检测,也不是版本检测,而是善用这个属性,确保顺序的使用条件:

    自定位,父无边(父亲也要定位)

    这样的话,所有浏览器的值都是引用的。

     

    总结:这6个属性要铭记于心,offsetLeftoffsetTop比较闹腾,但是合理使用,也没有兼容性问题。


    二、定时器

    2.1定时器

     window.setInterval(匿名函数,间隔时间);   //间隔定时器

     window.setTimeout(匿名函数,间隔时间);    //单次定时器

    第一个参数:函数,既可以是一个函数的函数名引用,也可以是一个匿名函数。不能加()

    第二个参数:时间间隔,单位是毫秒,1秒钟等于1000毫秒

    能够使每间隔时间,调用函数一次。习惯叫做定时器,按理说叫做“间隔器”。

    var i = 0;
    window.setInterval(function(){
       i++; //每间隔1000毫秒,执行一次函数
       console.log("间隔定时器,2秒执行一次,i的值:" + i );
    },1000);

    间隔时间是以毫秒为单位,1000毫秒就是1秒。

    “毫”就是千分之一,

    “厘”就是百分之一,

    “分”就是十分之一

    第一参数是函数,所以可以把一个匿名函数往里放,更可以用一个有名的函数的引用放里面:

    function fun(){
       alert("你好!");
    }
    window.setInterval(fun,1000);

    函数执行的方法:

    ①函数名或变量名加()执行。

    ②将一个函数绑定给某个事件,事件被触发,自动执行函数。

    ③将函数传给定时器的第一个参数,每隔时间间隔,自动执行函数。

    定时器的开启不需要任何关键字,只要程序能够执行到定时器部分,就会立即被开启,到第一个时间间隔后,会第一次执行函数。

    定时器是window对象的方法,可以省略window,所以:

    setInterval(function(){
       alert("你好!");
    },1000);

    单次定时器:

    setTimeout(function(){
       alert("boom~~~没有了");
    },1000);

    2.2简单运动模型

    视觉暂留:是一种视觉欺诈效果,人眼有视觉残留,每移动一步足够短,连续起来看起来就像在运动。残留时间是0.1秒到0.4秒。把连续相关的画面,连续播放,就是运动了。

    信号量编程:定义一个全局信号量,定义一个定时器,函数内部每执行一次,就让信号量自加,给css属性随时赋值。最终看起来就是在运动。

    var oBox = document.getElementById("box");
    var nowLeft = 0; //初始值
    setInterval(function(){
       //开启定时器,每间隔20毫秒执行一次函数,函数内部进行变量自加,并赋值
       nowLeft += 5;
       console.log(nowLeft);
       oBox.style.left = nowLeft + "px";
    },20);

    间隔时间是20毫秒,那么1秒执行函数50次,也就是说,这个动画是每秒50帧。

    控制简单运动速度的方法:

    1、增加每一步的步长可以加快速度,更改信号量自加的值。

    2、缩短间隔时间,相当于每一秒走的次数增加,1秒钟走的距离越远。Flash中有一个帧频的概念fps,每间隔多长时间走一帧,时间间隔如果是100毫秒,fps就是10

    注意性能问题:

    Chrome浏览器能够支持最小5的间隔时间,每秒200帧。

    IE6789只能支持最小50的间隔时间,每秒执行20帧。

    var nowLeft = 0; //初始值
    setInterval(function(){
       nowLeft += 5;
    },50);

    注意:简单运动,不需要知道走的总步长,只要知道每一步走的步长和间隔时间,就能实现。


    2.3清除定时器

     clearInterval()  清除间隔定时器

     clearTimeout()  清除单次定时器

    清除定时器时,要将定时器赋值给某个变量,停止时只要清除变量的引用即可。

    例如:clearInterval(定时器变量)

    var btn = document.getElementsByTagName("button");
    //开启间隔定时器
    var timer01 = null;
    var timer02 = null;
    btn[0].onclick = function(){
        timer01 = setInterval(function(){
           alert("2秒执行一次间隔定时器");
       },2000);
    }
    //开启单次定时器
    btn[1].onclick = function(){
       timer02 = setTimeout(function(){
           alert("单次定时器");
       },2000);
    }
    //清除间隔定时器
    btn[2].onclick = function(){
       clearInterval(timer01);
    }
     //清除间隔定时器
    btn[3].onclick = function(){
       clearTimeout(timer02);
    }

    2.4简单运动需要注意的事项

    问题1:如果将开启定时器的代码放在一个点击事件中,点击事情被多次触发,相当于开启了多个定时器,在一个时间点上有多个函数同时执行。而且timer变量是全局变量,点击一次相当于重新赋值,变量内部永远只能存最小的定时器,原来的定时器就没有了,不论怎么去定时器timer,都不能停止前面的定时器。

    var oBox = document.getElementById("box");
    var btn = document.getElementsByTagName("button");
    var now = 0; //全局信号量
    var timer = null; //存储定时器
    // 开启定时器运动
    btn[0].onclick = function(){
        // 每点击一次,开启定时器,让元素运动
        timer = setInterval(function(){
            now += 10;
            oBox.style.left = now + "px";
        },50);
    }
    // 停止定时器
    btn[1].onclick = function(){
        clearInterval(timer);
    }

    解决方法:设表先关,在事件内部定义应定时器之前,关掉之前的定时器,这样每次开启事件时,都会先清除一下timer之前存的定时器,放入一个新的定时器,后面停止时只需要停止最新定时器。

    // 开启定时器运动
    btn[0].onclick = function(){
        // 设表先关,避免多次点击按钮累加定时器,先关掉之前开启的定时器
        clearInterval(timer); //当点击事件触发后,立即清除timer
        // 每点击一次,开启定时器,让元素运动
        timer = setInterval(function(){
            now += 10;
            oBox.style.left = now + "px";
        },50);
    }

    问题2:当盒子到终点,自己停止,但是有时候步长设置不合理,不能正好停在固定值位置。

    下面方法是错误的:

    var oBox = document.getElementById("box");
    var nowLeft = 100; //初始值
    var timer = setInterval(function(){
       if(nowLeft < 600){//判断是否走到固定的位置,没走到继续,超过了停止。
           nowLeft += 13;
       }else{
           clearInterval(timer);
       }
       oBox.style.left = nowLeft + "px";
    },50);

    初始值100,所以盒子的运动轨迹是:100113126...607停止,盒子停下来的位置不是600,而是607

    解决方法:拉终停止,在定时器函数内部每次都要判断是否走到终点,走到终点先将变量值直接赋值一个终点值,然后停止定时器,拉到终点,停止定时器。

    var timer = setInterval(function(){
       nowLeft += 13;
       if(nowLeft > 600){//判断是否走到固定的位置,没走到继续,超过了停止。
           nowLeft = 600; //强制拉到终点
           clearInterval(timer); //停止定时器
       }
       oBox.style.left = nowLeft + "px";
    },50);

    三、无缝连续滚动

    3.1简单无缝滚动

    原理:页面上是6个图片,编号012345

    复制一倍在后面,长长的火车在移动:

     

    当你赋值的后半段火车的0号头贴到了盒子的左边框的时候,那么就

     

    瞬间移动到原点,重新执行动画:

     

    视觉欺诈效果:连个0的位置发生了互换,所有元素一样,看不出变化。

    var rolling = document.getElementById("rolling");
    var unit = document.getElementById("unit");
    //得到图片的数量,计算折返点,折返点就是210 * 图片数量(没复制之前的数量)
    var lisLength = unit.getElementsByTagName("li").length; //图片的数量
    var HTML = unit.innerHTML += unit.innerHTML; //复制一倍的li
    var timer = null; //存储定时器
    var nowLeft = 0;//初始值
    //鼠标移入停止定时器
    rolling.onmouseenter = function(){
        clearInterval(timer);
    }
    // 离开重新开启定时器
    rolling.onmouseleave = function(){
        move();
    }
    function move(){
        timer = setInterval(function(){
            nowLeft -= 3;
            //后验收,如果到了折返点,立即让left回到0的位置
            if(nowLeft < -210 * lisLength){
                nowLeft = 0;
            }
            unit.style.left = nowLeft + "px";
        },10);
    }
    move();

    3.2高级无缝滚动

    简单无缝轮播,使用的一些标签都是手动复制的,而且一些数值都是确定的值,如果一个标签发生变化,需要改的地方很多。程序耦合性太强,不能多个情况使用同一段js代码。

    改善:

    HTML结构中重复的代码,用js动态添加。

    ②折返点:不用计算,通过页面加载效果自动获取宽度,折返点的宽度应该等于ul内部所有元素宽度的一半。

    方法:li不要添加宽度,浮动元素被img自动撑宽,ul也不加宽度,绝对定位的元素用内部的li元素撑宽。

    下面的红箭头的长度,就是折返点的数值:

     

    解决方法有两个:

    方法1:遍历前半部分(复制一倍之前)所有的li,进行宽度累加,累加之后就是折返点。

    上午学的offsetWidth,这个方法不带margin。所以累加的时候,需要得到计算后的margin十分麻烦。所以不考虑方法1

    方法2:折返点就是假火车第1张图的offsetLeft值。所以,如果原来的li个数是lilength,那么假火车的第1张图就是lis[length]

    Chrome、火狐、IE10开始,不等图片加载完毕就执行代码,所以轮播图的li都没有宽度,li浮动了,浮动的父元素需要被子元素撑开宽高,图片有多宽li就有多宽。

    Chrome运行的时候,图片没有加载到,js就急着读取offsetLeft值,如何解决?

    解决方法:

    1、如需图片撑开元素宽度,保证图片是加载完毕,将所有代码写在window.onload事件中。

    2、图片加载事件image.onload

    var rolling = document.getElementById("rolling");
    var unit = document.getElementById("unit");
    //得到图片的数量,计算折返点,折返点就是210 * 图片数量(没复制之前的数量)
    var zhefandian; //折返点,图片原来的数量
    var HTML = unit.innerHTML += unit.innerHTML; //复制一倍的li
    var lis = unit.getElementsByTagName("li");  //得到li元素
    var imgs = document.getElementsByTagName('img'); //获取所有图片
    var lisLength = lis.length;//图片的数量
    // 判断图片是否加载完毕,如果加载完毕再计算offsetLeft值
    // 计算折返点,每个li宽度不同,所以家火车开头元素的offsetLeft就是折返点,这个元素lis[lisLength /
    // 但是由于浏览器执行代码不等图片加载完,所以要保证图片加载完后读取。
    var count = 0; //累加图片个数
    for(var i = 0; i < imgs.length;i++){
        imgs[i].onload = function(){
            count++; //如果加载成功累加1
            if(count == imgs.length){
                // 加载完毕得到折返点
                zhefandian = lis[lisLength / 2].offsetLeft;
                move(); //所有图片加载完毕再开始运动
            }
        }
    }
    var timer = null; //存储定时器
    var nowLeft = 0;//初始值
    //鼠标移入停止定时器
    rolling.onmouseenter = function(){
        clearInterval(timer);
    }
    // 离开重新开启定时器
    rolling.onmouseleave = function(){
        move();
    }
    function move(){
        timer = setInterval(function(){
            nowLeft -= 3;
            //后验收,如果到了折返点,立即让left回到0的位置
            if(nowLeft < -zhefandian){
                nowLeft = 0;
            }
            unit.style.left = nowLeft + "px";
        },10);
    }

    四、JSON

    4.1 最简单的JSON示例

    JSON叫做JavaScript Object NotationJavaScript对象表示法JS大牛Douglas发明。

    类似数组,内部也可以存放多条数据,数组只能通过下标获取某一项,有时不方便使用,json对象每一项数据都有自己的属性名和属性值,通过属性名可以调用属性值。

    JSON对象是引用类型值,所有是存储内存地址。

     

    之前学习过的数组:

     var arr = ["东风","西风","南风","北风"]

     数组很好用,arr[0]就是南风。但是发现,数组的下标,只能是阿拉伯数字,不能是我们任意取的。

     

    语法:

     {

     "k" : v,

     "k" : v

     }

    var obj = {
       "name":"小黑",
       "age":18,
       "sex":"不详",
       "height":190
    }
    console.log(typeof obj);
    console.log(obj);
    console.log(obj.age);   //18
    console.log(obj["age"]);//18

    调用某一项数据:

    1、通过obj变量名打“点”调用对应属性的属性名

     console.log(obj.age);

    2、将属性名的字符串格式放在[]进行调用

     console.log(obj["age"]);

    更改obj对象的某一项属性:就是调用属性名,通过“=”赋值

     obj.sex = "";


    4.2 JSON的嵌套

    JSON里面的v,可以是任意类型的值

    var obj = {
       "name":"黄晓明",
       "age":38,
       "sex":"不详",
       "height":160,
       "cp" :{
           "name" : "Angelababy",
           "age"  :16,
           "height":168
       }
    }
    // 所以想得到cp的age,以下写法都可以:
    console.log(obj)
    console.log(obj.cp)
    console.log(obj.cp.age);
    console.log(obj.cp["age"]);
    console.log(obj["cp"]["age"]);

    4.3 JSON的添加和删除

    如果想增加obj里面的项,那么就用“点”语法赋值:

    var obj = {
       "name":"黄晓明",
       "age":38,
       "height":160,
       "cp" :"杨颖"
    }
    obj.age++; //改变属性值
    obj.sex = "刚变性完"; //增加属性
    console.log(obj); 
    delete obj.cp;  //删除obj的cp属性
    console.log(obj);

    新增属性:添加新的属性,就用JSON对象的变量打点添加新的属性名,等号赋值。

    obj.cp = {
       "name" : "Angelababy",
       "age"  :16,
       "height":168
    }
    console.log(obj)

    删除某一个属性,使用delete关键字

     delete obj.cp; 


    4.4 JSON的遍历

    for..in循环语句,用于遍历数组或者JSON对象的属性(对数组或者JSON对象的属性进行循环操作)。

    for循环根据对象的属性名,从第一个开始进行遍历,直到遍历到最后一个属性,循环结束。

    语法:

     for(变量 in 对象){

        

     }

    遍历到最后一项,循环结束。k会依次等价于obj里面的属性名,在循环语句里,用obj[k]来读取属性值。

    var obj = {
       "name":"黄晓明",
       "age":38,
       "sex":"不详",
       "height":160,
       "cp" :{
           "name" : "Angelababy",
           "age"  :16,
           "height":168
       }
    }
    for(var k in obj){
       console.log(k +"的值是:"+ obj[k])
    }

    创建一个新的JSON,属性名和属性值与原有旧的JSON完全相同,要求不是指向同一个内存地址。

    不能直接用旧json的一个变量直接赋值给新的变量,否则就指向同一个内存地址。

    方法:创建一个新的sjon,内部数据为空,通过循环遍历旧的JSON,得到所有的属性名添加给新的JSON,然后给新的属性赋值。

    var obj1 = {
       "name":"黄晓明",
       "age":38,
       "sex":"不详",
       "height":160,
       "cp" :{
           "name" : "Angelababy",
           "age"  :16,
           "height":168
       }
    }
    // var obj2 = obj1; //这样会指向同一个内存地址,修改其中一个,两个变量的值都会改变
    // console.log(obj1 == obj2);
    var obj2 = {} //创建新的json对象,内存地址就不一样了
    // 遍历旧的json,获取所有的属性名和属性值
    // 等号左侧,给obj2的JSON添加属性
    // 等号右侧,将旧JSON属性取出来,赋值给新JSON对象的属性
    for(var k in obj1){
       obj2[k] = obj1[k];
       console.log(obj2)
    }
    console.log(obj1 == obj2);//结果false,所以修改obj1或2都不会互相影响

    五、同步异步和回调函数

    5.1同步和异步

    同步:synchronous

    程序从上到下执行:

     console.log(1);

     console.log(2);

     console.log(3);

     console.log(4);

    假如程序中有for循环,非常耗时间,但是浏览器会用“同步”的方式运行: 

    console.log(1);
    console.log(2);
    console.log(3);
    for(var i = 0;i < 1000;i++){
        console.log("★");
    }
    console.log(4);

    同步的意思:forwhile循环等很耗费时间,但是程序就傻等,等到1000个星星循环执行完毕,然后输出4

    比如用洗衣机洗衣服,需要等很长时间,等待的过程就是傻等,不同时做别的事情。

    异步:Asynchronous

    console.log(1);
    console.log(2);
    console.log(3);
    setInterval(function(){
        console.log("★");
    },100);
    console.log(4);

    出输4,提前执行了,然后输出星星

    “异步”的意思:遇见一个特别耗费时间的事情,程序不会傻等,而是先执行后面的代码,再回头执行异步的依据。

    比如用洗衣机洗衣服,需要等很长时间,等待的过程就是,可以做别的事情,比如扫地、做饭。

    JS中的异步语句:setIntervalsetTimeoutAjaxNodejs都是异步的。

    如果有异步语句,那么一定的是通过“异步”的方式执行代码,如果没有异步语句,就是同步方式执行。


    5.2回调函数

    异步的事情做完了,我们想继续做点什么事情,此时怎么办?

    回调函数:异步的语句做完后的事情。

    var count = 0;
    var timer = setInterval(function(){
       count++; //累加
       // console.log(count);
       console.log("★");
       if(count == 300){
           clearInterval(timer);
           callback(); //回调函数,等异步语句结束后,执行函数
       }
    },10);
    function callback(){
       alert("所有星星输出完毕");
       document.body.style.backgroundColor = "#000";
    }

    六、函数节流

    6.1 setTimeout()方法

    var oBox = document.getElementById("box");
    var oTip = document.getElementById("tip");
    oBox.onmouseenter = function(){
        oTip.style.display = "block"; //鼠标移入显示
    }
    oBox.onmouseleave = function(){
        setTimeout(function(){
            oTip.style.display = "none"; //鼠标移出,延迟1秒隐藏
        },1000);
    }

    6.2函数节流

    所谓的函数节流,就是我们希望一些函数不要连续的触发,甚至于规定,触发这个函数的最小间隔时间。

    这个就是“函数节流”。

    var lock = true; //开锁
    btn.onclick = function(){
       // 检测锁的开关情况,如果是false就执行return(后面的代码都不会执行),不执行这个事件
       if(lock == false){
           return;
       }
       lock = false; //上锁
       console.log(Math.random());
       setTimeout(function(){
           lock = true; //2000毫秒后开锁
       },2000);
    }

    优化写法:

    btn.onclick = function(){
       //检测锁开关情况,如果是false就执行return(后面代码都不会执行),不执行这个事件
       if(!lock){
           return;
       }
       lock = false; //关掉锁
       console.log(Math.random());
       setTimeout(function(){
           lock = true; //2000毫秒之后再开锁
       },2000);
    }

    七、callapply函数

    探讨普通函数中是否也有this关键字,发现普通函数的this的指向是window

    普通函数中this指向window对象。

    控制函数内部的this指向:

    函数都可以打点调用call()apply()方法,这两个方法可以帮我们指定函数内部的this指向谁。在函数调用过程使用这两种方法。

    var oBox = document.getElementById('box');
    function fun(){
        console.log(this);
    }
    // 两个作用
    // 1、执行fun函数
    // 2、在fun函数内部指定this指向div
    fun.call(oBox);
    fun.apply(oBox);
    var oBox = document.getElementById('box');
    function fun(a,b){
        this.style.backgroundColor = "pink";
        console.log(a,b);
    }
    fun.call(oBox,10,20);
    fun.apply(oBox,[10,20]);

    说白了,callapply功能是引用的,都是让函数调用,并且给函数设置this指向谁。

    区别:函数传递参数的语法。

     fun.call(oBox,10,20,30,40,50);

     fun.apply(oBox,[10,20,30,40,50]);

    call需要用逗号隔开罗列所有参数

    apply是把所有参数写在数组中,即使只有一个参数,也必须写在数组中。

    var obj = {
        "name":"小黑",
        "age" : 18,
        "sex" :"不详"
    }
    function showInfo(){
        console.log(this.name);
    }
    showInfo.call(obj);

  • 相关阅读:
    阿里消息队列中间件 RocketMQ 源码分析 —— Message 拉取与消费(上)
    数据库中间件 ShardingJDBC 源码分析 —— SQL 解析(三)之查询SQL
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 解析(六)之删除SQL
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 解析(五)之更新SQL
    消息队列中间件 RocketMQ 源码分析 —— Message 存储
    源码圈 300 胖友的书单整理
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 路由(一)分库分表配置
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 解析(四)之插入SQL
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 路由(二)之分库分表路由
    C#中Math类的用法
  • 原文地址:https://www.cnblogs.com/rope/p/10603219.html
Copyright © 2011-2022 走看看