js进阶_day04

目录

1.深浅拷贝

1.1浅拷贝

1.2深拷贝

2.异常处理

2.1throw抛异常

2.2try/catch捕获异常

2.3debugger

3.处理this

3.1this指向-普通函数

3.2this指向-箭头函数

3.3改变this

4.性能优化

4.1防抖(debounce)

4.2节流(throttle)


1.深浅拷贝

浅拷贝和深拷贝只针对引用类型

1.1浅拷贝

浅拷贝:拷贝的是地址

常见方法:

  1. 拷贝对象:Object.assign() / 展开运算符{...obj}拷贝对象
  2. 拷贝数组:Array.prototype.concat() / [...arr]
<script>
        const obj = {
            name: 'bjt',
            age: 18
        }
        // 浅拷贝
        // 第一种方法:
        // const o = {...obj}
        // console.log(o)  //{name: 'bjt', age: 18}
        // o.age = 20
        // console.log(o) //{name: 'bjt', age: 20}
        // console.log(obj) //{name: 'bjt', age: 18}

        // 第二种方法:
        const o = {}
        Object.assign(o, obj)
        o.age = 20
        console.log(o) //{name: 'bjt', age: 20}
        console.log(obj)  //{name: 'bjt', age: 18}
</script>

如果是简单数据类型拷贝值,引用数据类型拷贝的是地址(简单解释:如果是单层对象,没问题,如果是多层会存在问题)

1.2深拷贝

深拷贝:拷贝的是对象,不是地址

常见方法:

  1. 通过递归实现深拷贝
  2. lodash/cloneDeep
  3. 通过JSON.stringify()实现

1.通过递归实现深拷贝

函数递归:

如果一个函数在内部可以调用其本身,那么这个函数就是递归函数

  • 简单理解:函数内部自己调用自己,这个函数就是递归函数
  • 递归函数的作用和循环效果类似
  • 由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件return
<script>
        let i = 1
        // fn就是递归函数
        function fn() {
            console.log(`这是第${i}次`)
            if (i >= 6) {
                return
            }
            i++
            fn() //函数内部调用函数自己
        }
        fn()
</script>

常见使用场景:利用递归函数实现setTimeout模拟setInterval效果

<body>
    <div></div>
    <script>
        function getTime() {
            document.querySelector('div').innerHTML = new Date().toLocaleString()
            setTimeout(getTime, 1000)
        }
        getTime()
    </script>
</body>
<script>
        const obj = {
            uname: 'bjt',
            age: 18,
            hobby: ['basketball', 'football']
        }
        const o = {}
        // 拷贝函数
        function deepCopy(newObj, oldObj) {
            for(let k in oldObj) {
                // 处理数组问题
                if (oldObj[k] instanceof Array) {
                    newObj[k] = []
                    deepCopy(newObj[k], oldObj[k])
                } else {
                    // k(属性名) uname    oldObj[k]  属性值  bjt
                    // newObj[k] === o.uname
                    newObj[k] = oldObj[k]
                }
            }
        }
        deepCopy(o, obj)
        console.log(o) //{uname: 'bjt', age: 18, hobby:['篮球', 'football']}
        o.age = 20
        o.hobby[0] = '篮球'
        console.log(obj) //{uname: 'bjt', age: 18, hobby:['basketball', 'football']}
</script>

2.js库lodash里面cloneDeep内部实现深拷贝

<body>
    <!-- 必须先引用 -->
    <script src="./lodash.min.js"></script>
    <script>
        const obj = {
            name: 'bjt',
            age: 18,
            hobby: ['basketball', 'football'],
            family: {
                baby: 'small-bjt'
            }
        }
        const o = _.cloneDeep(obj)
        console.log(o)
        o.family.baby = 'small_白敬亭'
        console.log(obj)
    </script>
</body>

3.通过JSON.stringify()实现

<body>
    <script>
        const obj = {
            name: 'bjt',
            age: 18,
            hobby: ['basketball', 'football'],
            family: {
                baby: 'small-bjt'
            }
        }
        // 把对象转换成 JSON 字符串
        // console.log(JSON.stringify(obj))
        const o = JSON.parse(JSON.stringify(obj))
        console.log(o)
        o.family.baby = '123'
        console.log(obj)
    </script>
</body>

2.异常处理

2.1throw抛异常

异常处理是指预估代码执行过程中可能发生的错误,然后最大程度的避免错误的发生导致整个程序无法继续运行

<script>
        function fn(x, y) {
            if (!x || !y) {
                // throw '没有参数传递进来'
                throw new Error('没有参数传递进来')
            }
            return x + y
        }
        fn()
</script>

总结:

  1. throw抛出异常信息,程序也会终止执行
  2. throw后面跟的是错误提示信息
  3. Error对象配合throw使用,能够设置更详细的错误信息

2.2try/catch捕获异常

我们可以通过try / catch捕获错误信息(浏览器提供的错误信息)try试试 catch拦住finally最后

<body>
    <p>123</p>
    <script>
        function fn() {
            try {
                //可能发送错误的代码  要写到try
                const p = document.querySelector('.p')
                p.style.color = 'red'
            } catch(err) {
                // 拦截错误,提示浏览器提供的信息,但是不中断程序
                console.log(err.message)
                // 可以配合throw使用
                throw new Error('出现错误了')
                // return
            } finally {
                //不管程序对错,都会执行
                alert('弹出对话框')
            }
            //这里的代码还是会执行 除非在上面添加return
            console.log(11)
        }
        fn()
    </script>
</body>

总结:

  1. try...catch用于捕获错误信息
  2. 将预估可能发生错误的代码写在try代码段中
  3. 如果try代码段中出现错误后,会执行catch代码段,并截获到错误信息
  4. finally不管是否有错误,都会执行

2.3debugger

3.处理this

3.1this指向-普通函数

普通函数的调用方式决定了this的值,即“谁调用 this就指向谁”

<body>
    <button>点击</button>
    <script>
        // 一、普通函数:谁调用  this就指向谁
        // 1.
        console.log(this) //window
        // 2.
        function fn() {
            console.log(this) //window
        }
        // 完整写法是window.fn()
        fn()

        // 3.
        // 完整写法window.setTimeout
        setTimeout(function() {
            console.log(this) //window
        },1000)

        // 4.
        document.querySelector('button').addEventListener('click', function() {
            console.log(this) //指向button
        })

        // 5.
        const obj = {
            sayHi: function() {
                console.log(this) //指向obj
            }
        }
        obj.sayHi()
    </script>
</body>

普通函数没有明确调用者时,this指向window,严格模式下没有调用者this的值为undefined

3.2this指向-箭头函数

箭头函数中的this与普通函数完全不同,也不受调用方式的影响,事实上箭头函数中并不存在this

  1. 箭头函数会默认帮我们绑定外层this的值,所以在箭头函数中this的值和外层的this是一样的
  2. 箭头函数中的this引用的就是最近作用域中的this
  3. 向外层作用域中,一层一层查找this直到有this的定义

注意情况1:

在开发中【使用箭头函数前需要考虑函数中的this的值】,事件回调函数使用箭头函数时,this为全局的window,因此,DOM事件回调函数如果里面需要DOM对象的this,则不推荐使用箭头函数

// DOM节点
const btn = document.querySelector('.btn')
// 箭头函数 此时 this指向了window
btn.addEventListener('click', () => {
    console.log(this)
})
// 普通函数 此时this指向了 DOM对象
btn.addEventListener('click', function() {
    console.log(this)
}) 

注意情况2:

同样由于箭头函数this的原因,基于原型的面向对象也不推荐采用箭头函数

function Person() {
}
//原型对象上添加了箭头函数
Person.prototype.walk = () => {
    console.log('走路')
    console.log(this) //window
}
const p1 = new Person()
p1.walk()

总结:

  1. 函数内不存在this,沿用上一级的
  2. 不适用
    1. 构造函数,原型函数,dom事件函数等
  3. 适用
    1. 需要使用上层this的地方
  4. 适用正确的话,它会在跟多地方带来方便,后面对大量使用

3.3改变this

JavaScript中还允许指定函数中的this指向,有3个方法可以动态指定普通函数中的this指向

  • call()
  • apply()
  • bind()

1.call()-了解

使用call方法调用函数,同时指定被调用函数中this的值

语法:

fun.call(thisArg, arg1, arg2, ...)
  • thisArg:在fun函数运行时指定的this值
  • arg1, arg2:传递的其他参数
  • 返回值就是函数的返回值,因为它就是调用函数

2.apply()-理解

使用apply方法调用函数,同时指定被调用函中this的值

语法:

fun.apply(thisArg, [argsArray])
  • thisArg:在fun函数运行时指定的this的值
  • argsArray:传递的值,必须包含在数组里面
  • 返回值就是函数的返回值,因为它就是调用函数
  • 因此apply主要跟数组有关系,比如使用Math.max()求数组最大值
<script>
        const obj = {
            age: 18
        }
        function fun(x, y) {
            console.log(this)
            console.log(x + y)
        }
        fun.apply(obj, [1, 2])

        // 使用场景:求数组最大值
        const arr = [100, 20, 34, 35]
        const max = Math.max.apply(Math, arr)
        const min = Math.min.apply(Math, arr)
        console.log(max, min)
</script>

3.bind()-重点

bind()方法不会调用函数,但是能改变函数内部this指向

语法:

fun.bind(thisArg, arg1, arg2, ...)
  • thisArg:在fun函数运行时指定的this值
  • arg1,arg2:传递的其他参数
  • 返回由指定的this值和初始化参数改造的原函数拷贝(新函数)
  • 因此当我们只是想改变this指向,并且不想调用这个函数的时候,可以使用bind,比如改变定时器内部的this指向
// 需求:有一个按钮,点击就禁用,2秒以后自动开启
        const btn = document.querySelector('button')
        btn.addEventListener('click', function() {
            this.disabled = true
            setTimeout(function() {
                // 这里的this本来是指向window
                // 但是由于下面设置了bind方法,改变了this的指向
                this.disabled = false
            }.bind(btn), 2000) //这里可以写成btn或者this
        })

call apply bind总结

相同点:

都可以改变函数内部的this指向

区别点:

call 和 apply 会调用函数,并且改变函数内部this指向

call 和 apply 传递的参数不一样,call传递参数arg1,arg2...形式,apply必须数组形式[arg]

bind 不会调用函数,可以改变函数内部的this指向

主要应用场景:

call 调用函数并且可以传递参数

apply 经常跟数组有关系,比如借助于数学对象实现数组最大值最小值

bind 不调用函数,但是还想改变函数this指向,比如改变定时器内部的this指向

4.性能优化

4.1防抖(debounce)

单位时间内,频繁触发事件,只执行最后一次

使用场景:

  • 搜索框搜索输入。只需用户最后一次输入完成,再发送请求
  • 手机号、邮箱验证输入检测
<body>
    <div class="box"></div>
    <script src="./lodash.min.js"></script>
    <script>
        // 利用防抖实现性能优化
        // 需求:鼠标在盒子上移动,里面的数字就会变化 +1
        const box = document.querySelector('.box')
        let i = 1
        function mouseMove() {
            box.innerHTML = i++
            // 如果里面存在大量消耗性能的代码,比如dom操作,比如数据处理,可能造成卡顿
        }
        // 添加滑动事件
        // box.addEventListener('mousemove', mouseMove)


        // 利用lodash库实现防抖
        // 语法:_.debounce(fun, 时间)
        box.addEventListener('mousemove', _.debounce(mouseMove, 500))




        // 手写防抖函数
        // 核心是利用setTimeout定时器来实现
        // 1.先声明一个定时器变量
        // 2.每次鼠标移动(事件触发)的时候都要先判断是否有定时器,如果有先清除以前的定时器
        // 3.如果没有定时器,则开启定时器,存入到定时器变量里面
        // 4.定时器里面写函数调用
        function debounce(fn, t) {
            let timer
            return function() {
                if (timer) clearTimeout(timer)
                timer = setTimeout(function() {
                    fn()
                }, t)
            }
        }
        box.addEventListener('mousemove', debounce(mouseMove, 500))
    </script>
</body>

4.2节流(throttle)

单位时间内,频繁触发事件,只执行一次

使用场景:

高频事件:鼠标移动(mousemove)、页面尺寸缩放resize、滚动条滚动scroll等等

<body>
    <div class="box"></div>
    <script src="./lodash.min.js"></script>
    <script>
        // 利用节流实现性能优化
        // 需求:鼠标在盒子上移动,里面的数字就会变化 +1
        const box = document.querySelector('.box')
        let i = 1
        function mouseMove() {
            box.innerHTML = i++
            // 如果里面存在大量消耗性能的代码,比如dom操作,比如数据处理,可能造成卡顿
        }
        // 添加滑动事件
        // box.addEventListener('mousemove', mouseMove)


        // 利用lodash库实现防抖 - 500毫秒之后采取+1
        // 语法:_.throttle(fun, 时间)
        box.addEventListener('mousemove', _.throttle(mouseMove, 500))

        // 手写节流函数
        // 核心是利用setTimeout定时器来实现
        // 1.先声明一个定时器变量
        // 2.每次鼠标移动(事件触发)的时候都要先判断是否有定时器,如果有先清除以前的定时器
        // 3.如果没有定时器,则开启定时器,存入到定时器变量里面
        // 4.定时器里面写函数调用
        function throttle(fn, t) {
            let timer = null
            return function() {
                if (!timer) {
                    timer = setTimeout(function(){
                        fn()
                        timer = null
                    }, t)
                }
            }
        }
        box.addEventListener('mousemove', throttle(mouseMove, 500))
        
    </script>
</body>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值