5、JavaScript(三)

20.this对象

对于要绑定的多个对象的事件内容相同时可以使用循环来绑定,注意这时要使用this对象拿到当前调用函数的对象的属性和方法,不能直接使用循环变量作为角标。

1 this 对象基础内容

 <!-- 大坑坑坑坑!!!!!!!用循环给对象绑定相同的事件 -->

    <div>1</div>

    <div>2</div>

    <div>3</div>

    <div>4</div>

    <script>

        var divs = document.getElementsByTagName("div")

        for (var i = 0; i < divs.length; i++) {

            divs[i].onclick = function () {

                console.log(divs[i])//输出结果:undefined

                alert(divs[i].innerHTML)

            }

        }

        //运行结果显示:类型错误,由于循环内的divs[i]不是元素对象

        //1 点击事件是在循环结束后发生的

        //2 循环干了什么事?  给每个元素绑定了一个事件

        //3 i是全局变量 在某一时刻只能有一个值 在循环结束后i=4

        //在点击的时候才赋值,在点击之前循环只是给绑定了事件,并没有完成点击这个动作。当进行点击动作时循环已完成i变成了4,而元素对象的数组中并没有角标为4的

        //这时引出this对象

    </script>

<!-- 解决方法1:将点击事件中函数的divs[i]改为this对象 -->

<!--解决方法二:不用var ,使用let声明,因为let作用域-->

    <div>1</div>

    <div>2</div>

    <div>3</div>

    <div>4</div>

    <script>

        var divs = document.getElementsByTagName("div")

        for (var i = 0; i < divs.length; i++) {

            divs[i].onclick = function () {

                alert(this.innerHTML)//this指向的是divs[i],i=0,1,2,3

            }//原来不管谁点击,i的值都是4,alert输出的都是divs[4].innerHTML,而现在谁点击,alert输出的就是 谁的.innerHTML 点谁操作谁就用this

        }

        //运行结果显示:类型错误,由于循环内的divs[i]不是元素对象

        //1 点击事件是在循环结束后发生的

        //2 循环干了什么事?  给每个元素绑定了一个事件

        //3 i是全局变量 在某一时刻只能有一个值 在循环结束后i=4

        //在点击的时候才赋值,在点击之前循环只是给绑定了事件,并没有完成点击这个动作。当进行点击动作时循环已完成i变成了4,而元素对象的数组中并没有角标为4的

        //这时引出this对象

    </script>

this代表一个对象,this一般用在函数内部,this指向的是函数调用时所在的对象。

this对象在对一些对象绑定相同的事件时使用for循环的时候使用this对象,this对象是点谁谁做出改变,如果是点谁而另一个发生改变,这时就需要找到这二者之间的关系,找到相同的一些内容作为this对象指向的属性,通过this找到这个元素对象的属性,再通过该属性改变另一个元素对象的内容。

this指向的是函数运⾏时所在的对象(谁调⽤了函数,那么函数中的this就指谁(而不是在谁中定义的就指向谁)) ,如果调用的时没有指定谁调用,那么就是window全局对象调用。

<script>

        var obj1 = {

            name: "小明",

            intr: intr

        }

        var obj2 = {

            name: "小红",

            intr: intr

        }

        function intr() {

            console.log(this)

        }

        obj1.intr()//输出结果:小明的对象,obj1调用函数,这时的this指向的是obj1

        obj2.intr()//输出结果:小红的对象,obj2调用函数,this指向obj2

        intr()//输出结果:window的对象内容

        var name = "我是全局"

        var obj1 = {

            name: "小明",

            intr: function () {

                console.log(this.name)

            }

        }

        var c = obj1.intr

        c()//输出结果:我是全局

        /*因为obj1.intr并没有调用,只是将值取出来

        var c=function(){console.log(this.name)}

        等价于 function c(){console.log(this.name)}

        在这时调用c()等价于window.c(),这时this指向的就是window全局变量,输出的为全局变量name的值

        */

    </script>

2 window全局对象

我们定义的全局变量和全局函数本质上都是window的属性和方法,在访问的时候可以省略前缀。

        var a = 1

        console.log(a)//输出结果:1,二者等价

        console.log(window.a)//输出结果:1

3 this对象的实战:实现选项卡之间的切换

由于四个选项卡绑定事件的内容都相同,因此使用循环对这些元素对象绑定事件,注意使用this对象拿到当前调用函数的属性和方法,否则会出现点击事件在循环完成之后进行的,这时的循环变量在结束循环之后的值作为角标在元素对象数组中找不到与之对应的元素对象。

this点谁谁改变,点击后另一个元素对象发生改变,找到二者通过角标可以联系起来,因此将角标作为该元素的属性添加进去,通过this找到该元素的角标之后,通过角标对另一个元素对象做修改内容。

方法一:使用this的指向。

<!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>

    <style>

        * {

            margin: 0;

            padding: 0;

        }

        .tab {

            width: 80px;

            height: 50px;

            background-color: gray;

            float: left;

            margin-right: 5px;

            text-align: center;

            line-height: 50px;

            color: white;

        }

        .content {

            width: 335px;

            height: 300px;

            background-color: pink;

            display: none;

        }

        .act {

            display: block;

        }

    </style>

</head>

<body>

    <div class="tab">体育新闻</div>

    <div class="tab">科技新闻</div>

    <div class="tab">电影新闻</div>

    <div class="tab">娱乐新闻</div>

    <div style="clear: both;"></div>

    <div>

        <div class="content act">

            <p>我是体育新闻1</p>

            <p>我是体育新闻2</p>

            <p>我是体育新闻3</p>

        </div>

        <div class="content">

            <p>我是科技新闻1</p>

            <p>我是科技新闻2</p>

            <p>我是科技新闻3</p>

        </div>

        <div class="content">

            <p>我是电影新闻1</p>

            <p>我是电影新闻2</p>

            <p>我是电影新闻3</p>

        </div>

        <div class="content">

            <p>我是娱乐新闻1</p>

            <p>我是娱乐新闻2</p>

            <p>我是娱乐新闻3</p>

        </div>

    </div>

    <script>

        var tabs = document.getElementsByClassName("tab")

        var contents = document.getElementsByClassName("content")

        var index = 0

        //由于这四个的绑定的事件内容相同,使用循环和this对象

        for (var i = 0; i < tabs.length; i++) {

            tabs[i].index = i

            tabs[i].onclick = function () {

                //this是点谁谁变化,而我们要求的是点谁另一个变化

                //经过对比我们发现:点击的和要操作的角标相同,此时我们采用点击后拿到this.角标,通过该角标再对另一个操作

                //要得到角标的方法:将角标添加为属性,通过属性拿到角标值

                // contents[this.index].style.display = "block"

                //这时点击后会出现相应的内容,但此时点击之后之前点击的内容也会显示,为了让只显示当前点击的那个,我们采用当点击的时候把所有的都隐藏了,再将当前的显示就行

                for (var j = 0; j < contents.length; j++) {

                    contents[j].style.display = "none"

                    //点谁谁背景色变化,其余的不变,以便于能看出是点击了谁,同样是先将所有的tab变为灰色,再单独改变其中一个的背景色

                    tabs[j].style.backgroundColor = "gray"

                }

                contents[this.index].style.display = "block"

                tabs[this.index].style.backgroundColor = "red"

            }

        }

    </script>

</body>

</html>

方法二:使用let 。

        let tabs = document.getElementsByClassName("tab")

        let contents = document.getElementsByClassName("content")

        for (let i = 0; i < tabs.length; i++) {

            tabs[i].onclick = function () {

                // 先把所有的隐藏,再把当前的显示

                for (let j = 0; j < tabs.length; j++) {

                    tabs[j].style.backgroundColor = "gray"

                    contents[j].style.display = "none"

                }

                tabs[i].style.backgroundColor = "red"

                contents[i].style.display = "block"

            }

        }

21.定时器(运用的不好重点):window对象的方法

1 基本内容

定时器是让⽹⻚⾃动运⾏的唯⼀办法

1、周期性定时器:每隔⼀段时间,做什么事

setInterval(干什么事的函数名,间隔毫秒数)

2、⼀次性定时器:等待⼀定的时间,做什么事 ,就执行一次

setTimeout(干什么事的函数名,等待毫秒数)

同步:从上往下顺序执行,前面不走完后面不能走;

异步:

定时器是⼀个异步多线程的程序,每开一个定时器都增加一个线程,再加上本来JS就有的线程。而主线程永远是最先执行的(同步代码永远是最快的)

  setTimeout(function () {

            console.log(123)

        }, 3000)//等待3s

        console.log(456)

        //输出结果:先输出456,后输出123,因为定时器是异步多线程的

        setTimeout(function () {

            console.log(123)

        }, 0)//等待0s

        console.log(456)

        //输出结果:先输出456.后输出123,因为主线程的同步代码永远是最先执行的。

        setTimeout(function () {

            console.log(123)

        }, 1000)//等待1s

        setTimeout(function () {

            console.log(789)

        }, 500)//等待0.5s

        console.log(456)

        //输出结果:456 789 123

3、停⽌定时器

定时器的返回值是线程号,因此在使用定时器时如果是需要关闭的常常用一个变量将定时器存储起来。

clearInterval(线程号) 后面常常紧跟着 timer=null 用于释放内存

clearTimeout(线程号) timer=null

  var timer1 = setInterval(function () {

            console.log("aaa")

        }, 1000)

        var timer2 = setInterval(function () {

            console.log("bbb")

        }, 3000)

        clearInterval(timer1)

4、定时器的变量一般定义在全局,因为定时器的开启和关闭可能不再同一个函数中使用。

2 定时器案例

案例一:倒计时 周期性定时器

<!DOCTYPE html>

<head>

    <meta charset="UTF-8">

    <title>Document</title>

</head>

<body>

    <div id="txt"></div>

    <script>

        //作业:完善倒计时,将显示的时分秒改为:,且随着每秒的时间变化闪动

        /*案例一:倒计时 周期性定时器 距离下课还有多长时间

            用到了日期Date对象,需要计算时间差+定时器开启和停止

            注意1:开启了定时器之后要关闭定时器,当时间间隔大于0的时候才会进行单位换算并输出时间差,否则将定时器停止

            注意2:时分秒的显示格式完善,如将9显示为09

            注意3:将时分秒的显示改为冒号,且随着每秒的时间变化闪动

                    方法一:用flag 标记

                    方法二:用跳动的奇偶数

        */

        var txt = document.getElementById("txt")

        var timer = setInterval(clock, 1000)

        //结束时间的日期对象

        var targrt = new Date("2023-5-31 18:58:00")

        var col = ":"

        var flag = true

        function clock() {

            //当前日期对象

            var now = new Date();

            //时间间隔 返回的是毫秒数

            var ms = targrt - now

            //当时间间隔大于0时才做下面的内容,否则到了指定的时间点后应该停下来,停止定时器

            if (ms > 0) {

                //时间间隔时分秒换算

                var h = Math.floor(ms / 1000 / 60 / 60)//向下取整

                //时分秒格式完善,将9显示为09

                h = h < 10 ? "0" + h : h

                var min = Math.floor((ms - h * 60 * 60 * 1000) / 1000 / 60)

                min = min < 10 ? "0" + min : min

                var s = Math.floor((ms - h * 60 * 60 * 1000 - min * 60 * 1000) / 1000)

                s = s < 10 ? "0" + s : s

                if(flag){

                    col=":"

                }else{

                    col=" "

                }

                txt.innerHTML = "距离下课还有" + h + col + min + col + s

                flag = !flag

            } else {

                clearInterval(timer)

                txt.innerHTML = "下课了"

            }

        }

    </script>

</body>

</html>

案例二:获取验证码 周期性定时器

按钮禁用:button的属性disabled=”true”表示该按钮不可用。

按钮禁用时鼠标变为红色的圈:css属性 cursor:not-allowed(红色圈)/pointer(小手指)

<!DOCTYPE html>

<head>

    <meta charset="UTF-8">

    <title>Document</title>

</head>

<body>

    <div>

        <button id="btn">获取验证码</button>

    </div>

    <script>

        /* 定时器案例2:获取验证码 周期性定时器 点击一次按钮之后显示内容变为59 58 57 秒后重新发送,在此期间该按钮是无法点击的,直到60秒后才可重新点击

            补充1:按钮禁用:button的属性disabled="true"表示不可用。

            补充2:当按钮禁用时,鼠标变为红色的圈,

                   属于css样式:cursor:not-allowed(红色圈)/pointer(小手指)/default(默认的箭头)

            注意1:当点击一次按钮后就开启一个定时器,而此时的n是全局变量,会导致n的值快速变化,这时我们需要设置点击一次按钮后就将按钮禁用掉

            注意2:当n的值为0停止定时器后需要将按钮的状态和内容全都恢复成开始的样子

            注意3:在n的值为0后再次点击按钮,出现无法继续显示倒计时,这时因为此时n的值已经变为了0,需要将n的值恢复成初始的开始值

        */

        var btn = document.getElementById("btn")

        var timer;

        var n = 5//起始时间

        btn.onclick = function () {

            //在1秒的空隙内无法进行点击按钮

            btn.disabled = true

            btn.style.cursor = "not-allowed"

            timer = setInterval(clock, 1000)

        }

        function clock() {

            if (n > 0) {

                n--

                btn.innerHTML = n + "秒后重新获取验证码"

            } else {

                clearInterval(timer)

                btn.innerHTML = "重新获取验证码"

                btn.style.cursor = "auto"

                btn.disabled = false

                n = 5;//需要恢复n的值

            }

        }

    </script>

</body>

</html>

案例三:烦人的关不掉的小广告 周期性定时器+一次性定时器

<!DOCTYPE html>

<head>

    <meta charset="UTF-8">

    <title>Document</title>

    <style>

        #adv {

            position: fixed;

            right: 0;

            /* bottom的负值要比图片的高度要大,因为还有按钮的高度 */

            bottom: -335px;

        }

    </style>

</head>

<body>

    <div id="adv">

        <div>

            <button id="btn">关闭广告</button>

        </div>

        <img src="images/adv.jpg" alt="">

    </div>

    <script>

        /*定时器案例三:烦人的小广告 进入某个网站几秒后,右下角逐渐一点点的出来小广告,点击关闭按钮之后广告下去,之后几秒钟后就又出来了

        周期性定时器+一次性定时器

        注意1:将小广告放在右下角,以浏览器为参照物,使用固定定位,一开始bottom的值为负的,值为图片高度+按钮高度,让按钮和图片放在浏览器的下面不显示出来

        注意2:通过修改adv类的bottom值,周期性的往外走,实现逐渐出现的效果,使用周期性定时器

        注意3:通过adv.style.bottom是无法获取当前对象的bottom值的,因为通过这种方法获取的属性必须是以行内样式的形式定义的。

              获取元素的所有样式的方法:doucument.defaultView.getComputedStyle(要获取的元素对象,null)

        注意4:获取到当前的bottom值之后做加法时,bottom值是字符串类型,需要截取其中的数字,我们使用window的方法parseFloat()获取以数字开头的字符串的数字部分

        注意5:将bottom的单位px去掉之后,在给元素对象的bottom属性重新赋值时还需要再加上px

        注意6:在点击了关闭按钮之后等待三秒重新打开广告:执行的方法不能直接是moveUp,因为moveUp只执行一次是无法将小广告向上走,需要开启周期性定时器一步步向上走

        注意7:在广告上升的途中点击关闭按钮,会出现颤抖的现象,这时因为广告上升的途中定时器还未关闭,bottom还在加,而此时点击关闭按钮后又开启了一个定时器,bottom处在一下加10一下减10的过程中,这才导致的颤抖

               解决方法1:只能在广告全部出来以后才可以点击,在此期间不能点击。在关闭定时器后将timre=null释放内存同时做点击时能否做开启向下走的定时器的判断

               解决方法2:在上升途中允许点击关闭按钮,直接向下走:点击之后直接关闭定时器,再重新开启向下走的定时器

        */

        var adv = document.getElementById("adv")

        var btn = document.getElementById("btn")

        var timer1 = null;//存储小广告向上逐渐出现的周期性定时器,将周期性定时器的初始值设为null,等到使用的时候再用

        var timer2 = null;//存储一次性定时器,当进入网站就开启一次性定时器,3秒后就将小广告向上滑动

        // 小广告向上走的方法:每0.1秒向上移动一次,获取现在的bottom值,在此基础上做bottom的修改,而不是直接就给bottom赋值

        function moveUp() {

            //获取当前的bottom值,在此基础上每次加10

            var cssStyle = document.defaultView.getComputedStyle(adv, null)

            var bottom = parseFloat(cssStyle.bottom)//此时的bottom值为-325px,带有像素的单位

            //由于当前的bottom值带有px的单位,是字符串类型,如果直接+10,变成了-325px10,因此需要截取其中的数字部分,使用window的方法parseFloat()

            if (bottom < 0) {

                bottom += 10

                adv.style.bottom = bottom + "px"//给bottom再加上单位px

            } else {

                clearInterval(timer1)

                timer1=null//释放内存

            }

        }

        //向下走的方法

        function moveDown() {

            var cssStyle=document.defaultView.getComputedStyle(adv,null)

            var bottom=parseFloat(cssStyle.bottom)

            if(bottom>-335){

                bottom-=10

                adv.style.bottom=bottom+"px"

            }else{

                clearInterval(timer1)

                timer1=null

                //注意6:等待3秒钟重新打开广告,开启周期性定时器

                timer2=setTimeout(function(){

                    timer1=setInterval(moveUp,30)

                },3000)

            }

        }

        btn.onclick = function () {

            //注意7解决方法1:如果没有向上走的定时器即timer1=null,此时就可以点击关闭按钮关闭

            /*if(!timer1){

                timer1 = setInterval(moveDown, 30)

            }*/

            //注意7解决方法2:点击按钮之后直接关闭定时器,再重新开启向下走的定时器

            clearInterval(timer1)

            timer1 = setInterval(moveDown, 30)

        }

        //一打开网页并不会弹出小广告,等3秒钟再弹出

       window.οnlοad=function(){

            timer2=setTimeout(function(){

                timer1 = setInterval(moveUp, 30)

            },3000)

       }

    </script>

</body>

</html>

3 防抖(面试重点)⭐⭐⭐⭐⭐ 一次性定时器+闭包

发送网络请求,在短时间内连续触发多次事件(点击、鼠标移动、鼠标滚轮、键盘回车)只执行一次。

核心思想:延迟执行,把一次性定时器先停再开。只要点击的间隔小于给定的时间,就只执行一次,如果点击的间隔大于给定的时间,就会再次执行。

应用场景:购物车。

实现效果:在5秒内点击1000次,只能执行1次。

在项目中常常有很多地方都需要用到防抖,因此我们常常将防抖函数封装起来,然后把要做的事情作为参数传进去,在使用的时候只需要调用防抖函数并把要做的事情作为参数传进就行。

想要用这个变量,不能放局部否则会每次都更新,放全局的话又封装不彻底且全局污染 ,而且调用几次就需要定义几个标记,这时就可以采用闭包来实现。

防抖函数:

//防抖函数

        function debounce(fn){

            var timer=null;

            return function(){

               clearTimeout(timer)            

               timer=setTimeout(function(){

                    fn()//要做的事情

               },1000)

            }

        }

<!DOCTYPE html>

<head>

    <meta charset="UTF-8">

    <title>Document</title>

</head>

<body>

    <button id="btn">下单</button>

    <button id="load">加载更多</button>

    <script>

        var btn=document.getElementById("btn")

        var load=document.getElementById("load")

        // var timer1=null

        // var timer2=null

        /*多个按钮不能共享同一个定时器,否则会导致前面的刚开定时器,点击了后面的按钮之后就把人家前面的定时器停止了,

          如果是在全局声明多个定时器的变量,可以,但不建议,因为要少用全局变量有风险

          如果把timer声明成局部变量,这样每次在点击的时候都会重新声明一个timer=null,上一次点击使用的timer并没有被关闭,不可以,因此不能每次都重新声明timer

        */

       /* btn.οnclick=function(){

            //必须是先停再开,这样就保证了最后只开了一个定时器

            clearTimeout(timer1)

            timer1=setTimeout(function(){

                console.log("下单了")

            },1000)

        }

        load.οnclick=function(){

            clearTimeout(timer2)

            timer2=setTimeout(function(){

                console.log("加载更多")

            },1000)

        }*/

   

        //两个按钮独自占用一个timer,调用两次debounce函数,点击按钮时生成各自的timer,

        // btn.οnclick=debounce(function(){console.log("下单了")})

        btn.οnclick=debounce(a);//事件要么写匿名函数,要么写函数名,如果要传参数的话需要在匿名函数中重新写函数名+参数。而这里直接写的函数名+参数,这时因为debounce函数中执行结果的返回值正好是一个匿名函数

        load.οnclick=debounce(function(){console.log("加载更多数据")})

        /*封装防抖函数

            闭包:每个事件都要有自己的timer,既想要用timer的初始值,但是又不能每次执行时都重新声明,

        */

        function debounce(fn){

//返回的是匿名函数

            //每个要用的变量初始值,在调用该debounce函数时只声明一次timer=null,而下次执行debounce的时候就只执行返回的匿名函数

            var timer=null

            return function(){

                clearTimeout(timer)

                timer=setTimeout(function(){

//每次都要做的事情写在这里

                    //由于每个按钮要做的事情不一样,把要做的事情的函数作为参数传进来,然后执行的时候直接调用函数即可

                    fn()

                },1000)

            }

        }

        function a(){

            console.log("下单了")

        }

    </script>

</body>

</html>

4 节流(面试重点)⭐⭐⭐⭐⭐ 一次性定时器+闭包

在短时间内连续触发多次事件,会减少执行次数。发送了1000次请求,在1秒内最多执行一次,10s内最多执行10次,这样就由原来的1000次减少到了10次,实现了节流。

应用场景:淘宝放大镜。

实现效果:在1s内点击多次之后只能执行一次,在5秒内如果点击了1000次只能执行5次,减少了执行次数。

防抖是只执行最后一次,突然变化的;节流是减少执行次数,还是有过程 的,比如,淘宝的放大镜在鼠标滑过时会触发几百次的事件,但我们不用这么多次,只需要几十次就可以了,这时就可以采用节流。不采用防抖是因为我们还需要变化的过程,防抖是欻一下过去了,没有中间的过程变化。

关键思想:一有人进来,闸门立刻关闭,闸门每隔一定时间就会打开进下一个人,进来之后闸门再次关闭。

先判断闸门是否开着,若没开不能进,后续都不在执行直接return,若开着,进去后立刻关闭闸门 ,1秒后再打开闸门,

节流函数:

 //节流函数的封装

        function throttle(fn) {

            var flag = true//开门

            return function () {

                if (!flag) {

                    //如果是关门,则不用再继续了,后面的额事情不用在做了

                    return

                }

                flag = false//关门

                //1s之后再开门

                setTimeout(function () {

                    fn()

                    flag = true

                }, 1000)

            }

        }

<!DOCTYPE html>

<head>

    <meta charset="UTF-8">

    <title>Document</title>

</head>

<body>

    <!-- 作业1:封装节流函数 -->

    <button id="btn1">按钮1</button>

    <button id="btn2">按钮2</button>

    <script>

        var btn1=document.getElementById("btn1")

        var btn2=document.getElementById("btn2")

        btn1.οnclick=throttle(function(){console.log("123")})

        btn2.οnclick=throttle(function(){console.log("456")})

        //节流函数

        function throttle(fn){

            var flag=true

            return function(){

                if(!flag){

                    return

                }

                flag=false

                setTimeout(function(){

                    fn()

                    flag=true

                },1000)

            }

        }

    </script>

</body>

</html>

22.window对象的方法

1、定时器:setInterval(要做事情的函数,间隔时间) setTimeout(要做事情的函数,等待时间) clearInterval(线程号) clearTimeout(线程号)

2、parseInt(“数字开头的字符串”):获取字符串中的以整数开头的部分,而中间和结尾的都不行

parseFloat(“数字开头的字符串”):获取字符串当中以小数和整数开头的部分,负数也可以

3、onload() 当网页加载完毕之后再执行

我们常需要先加载网页的内容再加载网页的功能,如果想要将js代码写在head标签里面,或者写在body的前面时就需要这个方法,等到网页加载完毕之后再执js代码。

<script>

        window.οnlοad=function(){

            //将所有的js代码写在此处

        }

    </script>

Number(“数字”):将字符串类型的数字转换为数字类型的数字

23.原型、继承、原型链

1、原型(prototype):⽅法背后,专⻔保存由⽅法创建出来的对象的共有属性。

原型就是构造函数的属性,它指向的是一个对象,这个对象里放的就是共有的属性,原型的值也是一个对象。

只有函数才有原型(不管是不是构造函数都有原型)。对象都没有原型(×,函数也是对象)

使用构造函数创建的对象,其中的 属性值如果没有得到修改,那么该对象只能使用该属性而不能拥有改属性,其所有权归构造函数所有。(自己有用自己的,自己没有从父级上找)

2、创建对象的方式:

1.对象字⾯量的形式(最常用的,声明变量的形式) var obj={name:"⼩明",age:18}

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

3、构造函数/对象模板:专⻔⽤来创建相同结构对象的专⻔⽅法。(对象的结构相同只是属性值不一样)

构造函数的特点:

1.返回的一定是对象;

2.构造函数的函数名的首字母大写以此来区分于普通函数;

3.构造函数的调用:new 构造函数()

//自定义构造函数

        function Fn(a,b,c){

            this.a=a

            this.b=b

            ...

        }

//自己构造函数,给当前对象加属性

        function Student(name, age) {

            //给调用函数的当前对象添加一个name和age属性,其值分别为m和n

            this.name = name

            this.age = age

        }

        //构造方法的调用

        var xm = new Student("小明", 18)

        var xh = new Student("小红", 21)

        console.log(xm, xh)

4、在构造函数中 并没有使用return却能够返回,这是因为在使用new的时候由于new关键字的原理已经有了return。只要有new就有对象。

new关键字的原理:new关键字做了哪⼏件事?

1.创建了⼀个空对象并取出构造函数和参数 var obj={}

2.修改对象obj的隐式原型的指向为创造其的构造函数

3.改变this指向 把构造函数中的this从指向全局的window改为调用函数的对象obj,通过call、apply,因为取出来的参数的格式是数组类型的,适合用apply

4给该对象加属性 this.name="⼩ 明" this.age=18

5.返回⼀个全新的对象 return obj

5、关于属性:

共有属性:由同⼀构造函数创建出来的对象共同享有的属性。构造函数中的属性并不是共有属性

共有属性是只属于构造函数的,这些共有的属性只能存放在构造函数的原型中。

构造函数的原型指向一个对象,存储的就是共有属性。

⾃有属性:属于对象实例私有的属性

任何对象实例没有权利修改原型中的属性,只有构造函数本身能去修改原型。

共有属性只能放在原型中,其他的都不是。

//自己构造函数,给当前对象加属性

        function Student(name, age) {

            //给调用函数的当前对象添加一个name和age属性,其值分别为m和n

            this.name = name

            this.age = age

        }

        //添加共有属性,只有原型中的才是共有属性,自己加的只有一个共有属性car

        Student.prototype.car = "bmw"

        console.log(Student.prototype)//输出结果:{car: 'bmw', constructor: ƒ}

        //修改原型,修改共有属性,只能通过构造函数

        //Student.prototype.car="audi"

        //构造方法的调用

        var xm = new Student("小明", 18)

        var xh = new Student("小红", 21)

        xm.car = "benz"//xm的自有属性

        console.log(xm)//有三个属性,其car为benz而不是bmw

        console.log(xh)//有两个属性,没有car

        //能用car 但不能归其所有

        console.log(xm.car)//输出结果:benz,自己有先用自己的,自己没有从父级找

        console.log(xh.car)//输出结果:bmw

6、继承:使⽤现有类型,创建出新的类型,新的类型可以使⽤现有类型的属性和⽅法,也可以拓展出现有类型没有的属性和⽅法。

7、原型链

原型链:一个对象的隐式原型指向创建它的父级的原型,而父级也有隐式原型,也会指向他的父级的原型,由此往上一直找到null,这样的层层指向关系构成原型链。

作用:原型链的层层指向关系可以用来实现继承的准确性。

Function 代表的是所有函数function的⽗类 Object是所有对象的父类

Function是所有函数的祖先,但是不是所有对象的祖先,所有对象的祖先是Object,Function的祖先也是Object

__proto__:隐式原型。 任何⼀个对象都有隐式原型(只有null没有隐式原型),⽤来实现继承的,这种指向关系保证继承的准确性。

⼀个对象的隐式原型默认指向创建该对象的构造函数/父级的原型。 这种指向关系保证继承的准确性。

Object.prototype就已经找到头了,如果在Object.prototype中添加属性,则所有的对象都能使用。

如果某个属性或方法想要让所有的自定义对象/所有的数组/日期都能用,那么就把该方法放在原型中,这时它下面创建的所有自定义对象/数组/日期就都能用。

上图中所有的虚线都是原型链,实线不是。

24.给元素绑定事件的三种方式及移出事件

1 绑定事件的三种方式

1、eleObj.οnclick=function(){} 相当于给元素对象加属性,也可以在html中的开始标签中写。

2、元素开始标签里面绑定,不需要获取DOM元素 onclick <div οnclick=”fn(参数)”></div>

注意:在html中函数名要加上括号,在js中函数名加括号表示立即调用,而在html中不是。

3、元素对象.addEventListener("事件名",⽅法对象,是否在捕获阶段触发) 可以给元素对象绑定多个事件。

注意:添加事件监听 事件名为click(不是onclick,onclick是事件处理函数)

var btn = document.getElementById("btn")

        //要求点击之后要执行两个函数

        //方法一:将两个要执行的函数放在匿名函数中,在匿名函数中依次执行这两个函数

        /*btn.onclick = function () {

            fn()

            fn2()

        }*/

        //方法二:使用给元素绑定事件的第三种方式 既能同时给元素绑定多个事件、给元素绑定一个事件的多个功能。若采用绑定事件的第一种和第二种方法的方式,最后前面的事的都会被后面的覆盖

        btn.addEventListener("click", fn2, false)

        btn.addEventListener("click", function () { console.log(100) }, false)

        function fn() {

            alert(1)

        }

        function fn2() {

            console.log(5)

        }

2 删除事件

去掉点击事件:表单元素button才有的disabled=true将元素禁用,这种方法并不是所有的元素都有的。

方法一:通过第一种方式给元素绑定事件,将属性改为空null。

方法二:通过第二种方法给元素绑定事件,利用删除属性的方式,前提是必须是通过在元素开始标签中绑定的方式才能算是属性,否则在js中写的不能算是属性

方法三:通过第三种方法给元素绑定多个事件 利用removeEventListener方法。如果使用的是匿名函数,移出时方法对象如果与绑定时写的内容一样,并不能将事件移出。因为它们是对象,地址不一样,只是数据一样而已,移出的并不是原来的函数。

//去掉事件

        //方法一:通过第一种方式给元素绑定事件,将属性改为空null

        /* btn.onclick = fn2

         btn.onclick = null*/

        //方法二:通过第二种方法给元素绑定事件,利用删除属性的方式,前提是必须是通过在元素开始标签中绑定的方式才能算是属性,否则在js中写的不能算是属性

        /*btn.removeAttribute("onclick")*/

        //方法三:通过第三种方法给元素绑定多个事件 利用removeEventListener方法

        btn.addEventListener("click", fn2, false)

        btn.addEventListener("click", function () { console.log(100) }, false)

        btn.removeEventListener("click", fn2, false)

        // btn.removeEventListener("click", function () { console.log(100) }, false)

        //如果使用的是匿名函数,移除时方法对象如果与绑定时写的内容一样,并不能将事件移出。因为它们是对象,地址不一样,只是数据一样而已,移出的并不是原来的函数

3 事件触发周期(在嵌套的情况下讨论)(面试题)

事件触发周期:一个事件发生的整个过程。

三个阶段: 事件捕获阶段(外--⾥,统计谁有该事件,直到统计到目标之后就不再继续统计)--⽬标触发阶段--事件冒泡阶段(⾥--外,把之前统计的每一个都触发了)

有多层嵌套结构,其中有某些层绑定了相同的事件,当点击里面的某个块时,通过是否允许在捕获阶段触发来决定执行顺序。但在工作过程为中用到的false为多。

<body>

    <div class="d1">

        <div class="d2">

            <div class="d3"></div>

        </div>

    </div>

    <script>

        var divs = document.getElementsByTagName("div")

        for (var i = 0; i < divs.length; i++) {

            /*//采用第一种方法给元素对象绑定事件

            divs[i].onclick = function () {

                this.style.backgroundColor = "purple"

                alert(this.className)

                this.style.backgroundColor = ""//去掉行内样式的背景色,剩下的是内部样式的背景色,回归原来的背景色

            }

            //执行结果:点击最里面的d3块,弹出三个框,依次为d3、d2、d1。因为事件触发周期为事件捕获阶段(从外到里)、目标触发阶段、事件冒泡阶段(从里到外,最后执行的结果是从里层向外层的顺序)*/

            //采用第三种方法给元素对象绑定事件,通过addEventListener的最后一个参数是否允许在捕获阶段触发为true或false来决定里外层的执行顺序。

            divs[i].addEventListener("click", function () {

                this.style.backgroundColor = "purple"

                alert(this.className)

                this.style.backgroundColor = ""

            }, true)

            //执行结果:如果是否允许在捕获阶段触发为false,则在事件冒泡阶段从里到外,执行结果为d3、d2、d1;

            //        如果为true,则在事件触发周期的第一个阶段事件捕获阶段,从外到里的时候就要触发执行,执行结果为d1、d2、d3

        }

    </script>

</body>

由于事件触发周期中的事件冒泡阶段在点击里层的块时,包含其的外层块的事件也会被执行了,这就导致发生了bug,例如像表格中有一个编辑按钮,点击其可以对这一行进行编辑,该行又有点击该行可以复制的事件,这时如果点击了编辑按钮,就会出现先执行了编辑按钮的功能,之后又执行了点击该行做的操作,这两个事件都被触发了,这时就出现了一些错误,因此这就需要我们阻止事件冒泡的发生,由此引出下面的事件对象。

如: <table>

        <tr id="t">

            <td>

                <button id="btn">按钮</button>

            </td>

            <td>1</td>

            <td>2</td>

        </tr>

</table>

<script>

        var btn = document.getElementById("btn")

        var t = document.getElementById("t")

        btn.onclick = function () {

            console.log("btn被点击了")

        }

        t.onclick = function () {

            console.log("tr被点击了")

        }

        /*执行结果:点击里面的按钮,由于事件的触发周期的第三阶段事件冒泡阶段从里到外执行,button的事件被触发了,整行tr的事件也被触发了。

                    btn被点击了

                    tr被点击了

        */

    </script>

4 事件对象:用于解决事件冒泡的问题

1、事件对象:默认在事件触发的时候⾃动传⼊函数的第⼀个参数,与⽣俱来的,不是后天传⼊的。

不是所有的函数都有事件对象,只有是事件绑定的函数才有事件对象,例如采用匿名函数的情况:btn.οnclick=function(e){console.log(e);fn(10)}中的事件对象不是fn函数而是匿名函数的形参e。

2、事件对象中重要的属性和方法

(1)阻⽌冒泡: e.stopPropagation() 在发生冒泡的元素对象的触发事件中添加阻止冒泡的代码。

btn.onclick = fn;//这里不能传实参,e在调用时系统自动的传过去的

        function fn(e){

            console.log("btn被点击了")

            e.stopPropagation();//阻止冒泡,只有最内层的执行了,之前外层标记的都没有执行

        }

事件委托/代理:利⽤冒泡,如果嵌套元素都有相同的事件要触发,我们只需要在父级绑定事件,内部元素就可以使用事件,内部元素可以通过事件对象来准确拿到触发事件的那个对象。

事件委托的应用场景:如淘宝筛选用户动态选择,一开始不存在,当时获取不到元素对象,无法进行绑定事件,这时我们给外层的div绑定事件即可,通过事件对象来拿到之后点击的是哪一个。

 <!-- 冒泡的应用:事件委托在嵌套结构中点谁弹出谁的className -->

    <div class="d1">

        <div class="d2">

            <div class="d3"></div>

        </div>

    </div>

    <script>

        // 以前挨个获取,循环绑定,现在利用事件冒泡只给d1自己绑定即可

        var d1 = document.querySelector(".d1")

        d1.onclick = function (e) {

            //这里使用的是事件对象e,target,而不是this,因为this指向的是调用函数的元素对象,调用该函数的始终是d1,因此如果使用this输出的始终是d1。而e.target指向的是事件源对象,是点击了谁而导致发生的,点击谁显示谁。

            alert(e.target.className)

        }

    </script>

(2)访问事件源对象:e.target 。

(3)鼠标相对于当前块的偏移位置:e.offsetX e.offfsetY。

(4)按键码:e.keyCode 键盘按下区分按下的是哪一个键 如回车13、删除8

例如:使用上下左右的键盘控制圆的位置。

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <title>Document</title>

    <style>

        #bird {

            position: absolute;

            width: 100px;

            height: 100px;

            background-color: red;

            border-radius: 50%;

        }

    </style>

</head>

<body>

    <!-- 给整个页面帮绑定键盘事件  使用上下左右的键盘按键控制圆的位置 -->

    <div id="bird"></div>

    <script>

        var bird = document.getElementById("bird")

        //给整个的页面绑定键盘事件

        document.onkeydown = function (e) {

            //拿到当前圆的位置 拿到所有的样式

            var cssStyle = document.defaultView.getComputedStyle(bird, null)

            var top = parseFloat(cssStyle.top)

            var left = parseFloat(cssStyle.left)

            //上38 下40 左37 右39

            if (e.keyCode == 38) {

                top -= 10

                bird.style.top = top + "px"

            } else if (e.keyCode == 40) {

                top += 10

                bird.style.top = top + "px"

            } else if (e.keyCode == 37) {

                left -= 10

                bird.style.left = left + "px"

            } else if (e.keyCode == 39) {

                left += 10

                bird.style.left = left + "px"

            }

        }

    </script>

</body>

</html>

5 e.target VS this

this指向的是调用函数的对象,会随着调用函数的变化而变化,this的指向也可以发生变化,而e.target指向的始终是事件源对象,不会发生改变。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/450793.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

arm架构ceph pacific部署

背景 合作伙伴实验室的华为私有云原来使用单点的nfs做为存储设备&#xff0c;现有两方面考量&#xff0c;业务需要使用oss了&#xff0c;k8s集群及其他机器也需要一套可扩展的分布式文件系统 部署ceph 初始机器配置规划 IP配置主机名Role10.17.3.144c8g1T数据盘ceph-node01…

录屏不再难!四款免费电脑桌面录屏工具测评报告

作为一个办公室文员&#xff0c;我经常需要录制一些操作演示视频或者会议记录。最近&#xff0c;我尝试了四款免费的电脑桌面录屏工具&#xff1a;福昕录屏大师、转转大师录屏、爱拍录屏和Screen Studio。今天就来跟大家分享一下我的使用体验&#xff0c;希望能帮到和我有同样需…

vue综合指南(六)

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;Vue篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来Vuet篇专栏内容:vue综合指南 目录 101、Vue 框架怎么实现对象和数组的监听&#xff1f; 102、Proxy 与 Object.d…

AntV X6自定义连接线样式(Vue3+TypeScript)

效果图如下&#xff1a;&#xff08;连接线是有动画的&#xff0c;模拟数据传输特效&#xff09; 核心代码&#xff1a; 在创建画布的时候即可设置连接线样式&#xff0c;通过createEdge属性即可实现&#xff0c;代码如下&#xff1a; connecting: {snap: {radius: 50, //自动吸…

工业相机详解及选型

工业相机相对于传统的民用相机而言&#xff0c;具有搞图像稳定性,传输能力和高抗干扰能力等&#xff0c;目前市面上的工业相机大多数是基于CCD&#xff08;Charge Coupled Device)或CMOS(Complementary Metal Oxide Semiconductor)芯片的相机。 一&#xff0c;工业相机的分类 …

Vivado HLS学习

视频链接: 6课&#xff1a;数据类型的转换_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1bt41187RW?spm_id_from333.788.videopod.episodes&vd_sourcea75d5585c5297210add71187236ec90b&p6 目录 1.数据类型的转换 2.自动类型转换 2.1隐式数据转换 2.2…

Windows安装Minio服务器端

Windows安装Minio服务器端 Windows安装Minio服务器端启动minio server将minio封装成系统服务默认账号密码常用参数 常见问题Minio为客户提供NFS调整minio文件分享时长设置“桶”为公开&#xff0c;如图设置即可 使用minio命令行客户端配置mcmc命令行常用方法创建bucket查看buck…

离散数学-逻辑与证明基础1.4(谓词和量词)

谓词 1.4.2 谓词 涉及变量的语句&#xff0c;例如&#xff1a; “ x > 3 x > 3 x>3”&#xff0c;“ x y 3 x y 3 xy3”&#xff0c;“ x y z x y z xyz” 以及 \quad “Computer x x x is under attack by an intruder” \quad “Computer x x x is f…

安装CentOS 8镜像和创建CentOS 8虚拟机教程

一、安装虚拟机 网上查找教程&#xff0c;我用的是VMware 17 二、下载CentOS 8镜像 1.阿里云下载CentOS 8镜像 centos安装包下载_开源镜像站-阿里云 (aliyun.com) 选择需要下载的版本&#xff0c;(建议)下载dvd1版本的iso&#xff08;也有下载boot版本的iso&#xff0c;创…

【进阶OpenCV】 (18)-- Dlib库 --人脸关键点定位

文章目录 人脸关键点定位一、作用二、原理三、代码实现1. 构造人脸检测器2. 载入模型&#xff08;加载预测器&#xff09;3. 获取关键点4. 显示图像5. 完整代码 总结 人脸关键点定位 在dlib库中&#xff0c;有shape_predictor_68_face_landmarks.dat预测器&#xff0c;这是一个…

【汇编语言】寄存器(内存访问)(二)—— DS和[address]

前言 &#x1f4cc; 汇编语言是很多相关课程&#xff08;如数据结构、操作系统、微机原理&#xff09;的重要基础。但仅仅从课程的角度出发就太片面了&#xff0c;其实学习汇编语言可以深入理解计算机底层工作原理&#xff0c;提升代码效率&#xff0c;尤其在嵌入式系统和性能优…

day46|72. 编辑距离647. 回文子串516.最长回文子序列 5 最长回文子串

文章目录 前言72. 编辑距离思路方法一647. 回文子串思路方法一方法二516.最长回文子序列思路方法一5 最长回文子串总结前言 72. 编辑距离 思路 总体思路:dp定义直接为操作数,递推公式分情况讨论,如果两个元素相等,那操作数不变;如果不相等,那么操作数就会改变–三种情况…

免费证件照app哪个好?哪个效果比较好?

在日常生活中&#xff0c;证件照的需求无处不在&#xff0c;尤其是在求职、签证和考试等场合。 许多人可能会觉得制作证件照需要花费不少费用&#xff0c;但其实市场上有许多免费的证件照制作软件&#xff0c;能够轻松满足你的需求。 这些软件不仅操作简单&#xff0c;更具备…

如何在word里面给文字加拼音?

如何在word里面给文字加拼音&#xff1f;在现代社会&#xff0c;阅读已经成为了我们日常生活中不可或缺的一部分。尤其是在学习汉语的过程中&#xff0c;拼音的帮助显得尤为重要。为了帮助大家更好地理解和掌握汉字的发音&#xff0c;许多教师和学生都希望能够在Word文档中为文…

什么是网络代理

了解网络代理 网络代理是一种特殊的网络服务&#xff0c;它允许一个网络终端&#xff08;通常指客户端&#xff09;通过这个服务与另一个网络终端&#xff08;通常指服务器&#xff09;进行非直接的连接。网络代理服务器位于发送主机和接收主机之间&#xff0c;接收网络请求&a…

使用人体关键点驱动FBX格式虚拟人原理【详解】

文章目录 1、使用人体关键点数据驱动FBX格式虚拟人的总流程2、使用mediapipe检测人体关键点和插值平滑2.1 mediapipe检测人体关键点2.2 人体关键点的插值平滑 3、将2d关键点转为3d关键点4、旋转矩阵4.1 旋转矩阵4.2 旋转矩阵转为四元数 5、将旋转矩阵用于虚拟人的驱动5.1 基础旋…

高分SCI发文利器!植物脂质代谢数据库——CLAIR

脂质是全球重要的大宗商品和工业原料。世界上消耗的24种脂质中&#xff0c;大部分来自植物。全面了解不同油料作物中与脂质生物合成相关的基因和机制&#xff0c;对于通过分子生物学和育种来提高这些作物的含油性状至关重要。 2024年2月&#xff0c;Plant Communications在线发…

台积电Q3业绩猛增,市值破万亿美元!

KlipC报道&#xff1a;全球晶圆代工龙头台积电发布第三季度财报&#xff0c;财报显示&#xff0c;三季度营收7596.9亿新台币&#xff08;约235亿美元&#xff09;&#xff0c;同比增长39%&#xff0c;市场预期7421.66亿元新台币&#xff1b;净利润达到3252.58亿新台币&#xff…

【可答疑】基于51单片机的智能衣柜(含仿真、代码、报告、演示视频等)

✨哈喽大家好&#xff0c;这里是每天一杯冰美式oh&#xff0c;985电子本硕&#xff0c;大厂嵌入式在职0.3年&#xff0c;业余时间做做单片机小项目&#xff0c;有需要也可以提供就业指导&#xff08;免费&#xff09;~ &#x1f431;‍&#x1f409;这是51单片机毕业设计100篇…

云计算第四阶段: cloud二周目 07-08

cloud 07 一、k8s服务管理 创建服务 # 资源清单文件 [rootmaster ~]# kubectl create service clusterip websvc --tcp80:80 --dry-runclient -o yaml [rootmaster ~]# vim websvc.yaml --- kind: Service apiVersion: v1 metadata:name: websvc spec:type: ClusterIPselector…