JavaScript(五)

104 阅读6分钟

定时器

定时器是让网页自动运行的唯一办法

定时器是window对象的方法

定时器分类

周期性定时器

每隔一段时间,做什么事

setInterval(函数名/函数体,间隔毫秒数)

/*周期性定时器*/
function fn(){
  console.log(1);
}
setInterval(fn,1000);

一次性定时器

等待一定的时间,做什么事

setTimeout(函数名/函数体,等待毫秒数)

/*一次性定时器*/
function fn(){
  console.log(1);
}
setTimeout(fn,1000);

定时器是一个异步多线程的程序

setInterval(function(){
            console.log(1);
        },2000)
setInterval(function(){
            console.log(2);
        },1000)
console.log(3)
//输出:3 2 1
//先输出3可以证明同步的程序永远比异步快
//先输出2可以证明是个异步的程序

停止定时器

定时器的执行结果是线程号,第一个定时器线程号是1,第二个定时器线程号是2,以此类推

停止计时器分类

周期性定时器的停止定时器

clearInterval(线程号)

线程号=null //释放内存

一次性定时器的停止定时器

clearTimeout(线程号)

线程号=null //释放内存

var btn = document.getElementById("btn");
//如果定时器想要停,一定要定义一个线程号
var index = setInterval(function(){
  console.log("执行了");
},1000)
btn.onclick=function(){
  clearInterval(index);
  index=null;
}

定时器案例

案例一

 <h1 id="txt"></h1>
    <!-- 倒计时案例 -->
    <script>
         var target = new Date("2022-11-11 00:00:00");
         var txt = document.getElementById("txt");
         function clock(){    
            var now=new Date();
            var ms=target-now;
            if(ms>0){
            var d = Math.floor(ms/1000/60/60/24);
            d=d<10?"0"+d:d;
            var h = Math.floor((ms-d*1000*60*60*24)/1000/60/60);
            h=h<10?"0"+h:h;
            var m = Math.floor((ms-d*1000*60*60*24-h*1000*60*60)/1000/60);
            m=m<10?"0"+m:m;
            var s = Math.floor((ms-d*1000*60*60*24-h*1000*60*60-m*1000*60)/1000);
            s=s<10?"0"+s:s;
            if(s%2==0){
                txt.innerHTML="距离双十一还有"+d+":"+h+":"+m+":"+s;
            }else{
                txt.innerHTML="距离双十一还有"+d+" "+h+" "+m+" "+s;
            }
            
        }else{
            clearInterval(timer);
            alert("三二一,上链接!");
        }
    }
         var timer=setInterval(clock,1000);
    </script>

案例二

    <button id="btn">获取验证码</button>
    <!-- 获取验证码 -->
    <script>
        // 定时器
        var timer = null;
        // 获取元素
        var btn = document.getElementById("btn");
        // 绑定事件
        btn.onclick=function(){
            btn.setAttribute("disaled",true);
            timer=setInterval(fn,1000);
        } 
        // 定时器干的事
        var n = 6;
        function fn(){
            if(n>0){
                btn.innerHTML= n+"s后重新发送";
                n--;
            }else{
                clearInterval(timer);
                timer=null;
                btn.innerHTML= "获取验证码";
                btn.disabled= false;
                n=6;
            }
        }
    </script>

案例三

<div>
        <div>关闭</div>
        <img src="img/ad.jpg" alt="" id="adv">
    </div>
    <script>
        //一次性定时器
        var timer1 = null;
        //周期性定时器
        var timer2 = null;
        //向上走的定时器函数
        function moveUp(){
            //document.deComputedStyle()方法可以获得到元素的所有样式
            var cssStyle = document.defaultView.getComputedStyle(adv,null);
            var bottom = parseInt(cssStyle.bottom);
            /*parseInt()截取字符串中以整数开头的部分
              parseFloat()截取字符串中以整数开头的部分*/
            if(bottom<0){
                bottom+=5;
                //DOM操作
                adv.style.bottom=bottom+"px";
            }else{
                clearInterval(timer2);
                timer2=null;
            }
        }
        //向下走的方法
        function moveDown(){
            var cssStyle = document.defaultView.getComputedStyle(adv,null);
            var bottom = parseInt(cssStyle.bottom);
            if(bottom<-198){
                bottom-=5;
                //DOM操作
                adv.style.bottom=bottom+"px";
            }else{
                clearInterval(timer2);
                timer2=null;
                //重新打开周期性计时器
                timer=setInterval(function(){
                    timer2=setInterval(moveUp,10)
                },3000)
            }
        }
        window.onload=function(){
            timer=setTimeout(function(){
                timer2=setInterval(move,10)
            },1000)
        }
        //关闭事件
        close.onclick=function(){
            if(timer2===null){
                timer2=setInterval(moveDown,10)
            }   
        }
    </script>

防抖和节流

节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效

防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时

//封装防抖函数

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button id="btn">防抖</button>
    <script>
        var btn = document.getElementById("btn");
        //要做的事1
        function one(){
            console.log(1);
        }

         //要做的事2
         function two(){
            console.log(2);
        }

        //封装的防抖函数
        function dobounce(fn){
            var timer=null;
            return function(){
                clearTimeout(timer);
                //定时器是延缓了点击事件的运行
                timer=setTimeout(function(){
                    fn();
                },500)
            }
        }
        //dobounce() 的调用结果就是一个函数
        btn.onclick=dobounce(one);
    </script>
</body>
</html>

//封装节流函数
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button id="btn">节流</button>
    <script>
        //节流:用一个一次性定时器来延缓运行

        var btn = document.getElementById("btn");
        //要做的事1
        function one(){
            console.log(1);
        }

         //要做的事2
         function two(){
            console.log(2);
        }

        function throttle(fn){
            var flag=true//flag = true门是开的
            return  function(){
            if(!flag){
                return;
            }
            flag = false;//门关上了,但是这个进来的人还能往下运行

            setTimeout(function(){
                console.log(1);
                flag = true;//这个人运行完再关上
            },500)
        }
    }
    btn.onclick=throttle(one);
    </script>
</body>
</html>

原型与继承

原型(prototype):方法背后,专门保存由方法创建出来的对象的共有属性

1.对象字面量的形式 var obj={name:"小明",age:18}

2.通过构造函数的形式 new Date() new Array() new Object() new RegExp()

构造函数/对象模板:专门用来创建相同结构对象的专门方法

//构造函数名要大写
//内置的构造函数只能创造出内置对象,例如:日期函数只能创造出日期对象
var ll={name:"李雷",age:18}
        var hmm={name:"韩梅梅",age:17}

        //构造函数
        function Student(name,age){
            //this表示当前对象
            this.name=name;
            this.age=age;
        }
        //想让李雷和韩梅梅同时拥有共有属性car,存在原型里
				//共有属性:由同一构造函数创建出来的对象共同享有的属性
        Student.prototype.car="g63";
        //构造函数必须通过new调用
        //new + 构造函数 一定得到一个对象
        var ll = new Student("李雷",18);
        var hmm = new Student("韩梅梅",17);
        //car自有属性:属于对象实例自身私有的属性
        ll.car="rangerover"
	//李雷的自由属性,但是没有改变原型中的共有属性任何对象没有权利修改原型中的属性
      	/*
          new关键字做了哪几件事?
          1.创建了一个空对象    var ll={}
          2.改变this指向 call apply
          3.加属性 this.name="小明" this.age=18
          4.返回一个全新的对象   return ll
       */
      	

继承:使用现有类型,创建出新的类型,新的类型可以使用现有类型的属性和方法,也可以拓展出现有类型没有的属性和方法(青出于蓝而胜于蓝)

一个对象用了某个属性/方法,不一定是他有这个属性或方法,可能是继承的

原型链

Function(大写) 代表的是所有函数的父类

Object 是所有对象的父类

原型:任何函数都有原型(object)

proto:隐式原型。 任何一个对象都有隐式原型,用来实现继承的 一个对象的隐式原型默认指向创建该对象的构造函数的原型对象

function Student(name,age){
            this.name=name;
            this.age=age;
        }
	var ll = new Student("李雷",18);
	//true  保证了继承
	console.log(ll._proto_===Student.prototype); 

image.png

instanceof方法:用于检测某个属性是否出现在某个实例的原型链上,只适用于引用类型

typeof无法检测出数组和对象,但是instanceof可以

子孙 instanceof 祖宗 返回如果在一条原型链返回true,否则返回false

function Student(name,age){
            this.name=name;
            this.age=age;
        }
var ll=new Student("李雷",18)
console.log(ll instanceof Student);//true
//instanceof方法的源码

function _instanceof(child,parent){
            //子类的隐式原型默认指向祖先的原型对象
            var left = child.__proto__;
            //父类的原型对象
            var right=parent.protptype;
            while(true){
                //代表已经找到原型链的尽头null也没找到和right相等的left
                if(left===null){
                    return false;
                }
                //左右相等,返回true
                if(left===right){
                    return true;
                }

                //如果父级不相等就找父级的父级,以此类推
                left=left.__proto__;
            }
              
        }

new关键字的原理

apply() call() bind() 内置函数

作用

用来主动改变this指向

语法

/*
call()
方法.call(对象,参数1,参数2,...);

apply()
方法.apply(对象,[参数1,参数2,...]);

bind()会返回一个函数,需要主动调用才能执行
var fn = 方法.bind(对象);//不用的时候先借过来
fn(参数1,参数2,...);//想用的时候不限次数的用
fn(参数1,参数2,...);
fn(参数1,参数2,...);
*/

var ll={
            name:"李雷",
            age:18,
            car:function(oil){
                console.log(this.name+"开李雷的车"+"加了"+oil+"元的油");
            }
        }
        var zs={
            name:"张三",
            age:18
        }
        var ls={
            name:"李四",
            age:18
        }
        var ww={
            name:"王五",
            age:18
        }
        ll.car.call(zs,500);
        ll.car.apply(ls,[300]);
        var fn = ll.car.bind(ww);
        fn(1000);
        fn(2000);

new关键字的原理

arguments 实参的集合(不是数组,但是类似数组,有length,也可以用下标找到其中的数据),当函数参数个数无法确定的时候,用arguments

function _new(){
  //1.创建一个空对象
  var obj={};
  //2.将类数据agrument赋予数组的shift方法,此时的fn是构造函数
  var fn=[].shift.call(arguments);
  //3.将对象的隐式原型指向父类的原型
  obj.__proto__=fn.prototype;
  //4.改变this指向,调用方法
  fn.apply(obj,arguments);
  //5.返回对象
  return obj;
}
function Student(name,age){
  this.name=name;
  this.age=age;
}
Studeht.prototype.car="benz";

var ll = _new(Student,"小明",18);
console.log(ll.car);

绑定事件的第三种方式

绑定事件

作用

给元素绑定多个事件

语法

元素对象.addEventListener("事件名",方法对象,是否在捕获阶段触发)

事件名:例onclick叫事件处理函数,click叫事件名

是否在捕获阶段触发:

false:禁止在捕获阶段触发

true:在捕获阶段触发(从外往里)

移除事件

//移除事件: removeEventListener();

btn.addEventListener("click",function(){
  console.log(1);
},false)
btn.removeEventListener();

事件触发周期

事件触发周期

事件对象:默认在事件触发时自动传入函数的第一个参数,与生俱来的,不是后天传入的,有事件发生才有事件对象

function fn(m){
    console.log(m);
}
divs[0].onclick=function(){
  fn();//有事件发生才返回事件对象
}

事件捕获(外--里)--目标触发--事件冒泡(里--外)

事件捕获:从外往里做标记

事件冒泡:从里往外冒

事件冒泡

事件冒泡:指父元素和子元素有相同的事件,当触发子元素事件时,会向上冒泡,同时也会触发父元素事件

阻止冒泡: e.stopPropagation()

divs[0].onclick=function(e){
  e.stopPropagation();
}

事件委托

定义

把原本需要绑定在子元素的响应事件委托给父元素,让父元素担当事件监听的职务

原理

事件代理的原理是DOM元素的事件冒泡

应用场景:

1.多个嵌套的父子元素需要绑定相同的事件

2.如果元素的绑定在事件触发之后,必须使用事件委托

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

        box.onclick=function(e){
            console.log(e.target.className);
        }
        box.innerHTML="<div class='d2'></div>";