老己的JavaScript 高级笔记 4

0 阅读7分钟

JavaScript高级 第四天

一、深浅拷贝

对对象属性进行赋值,容易改变原来属性的值

eg:

const pink = {
    name:'pink老师',
    age:18
}
const red = pink
console.log(red) //{name:'pink老师',age:18}
red.name = 'red老师'
console.log(red) //{name:'red老师',age:18}
console.log(pink) //{name:'red老师',age:18}

1.浅拷贝

浅拷贝:拷贝的是地址

常见方法:

  1. 拷贝对象: Object.assgin () / 展开运算符 obj } 拷贝对象

  2. 拷贝数组: Array.prototype.concat () 或者 arr

eg:

const pink = {
    name:'pink老师',
    age:18
}
const red = {}
Object.assign(red,pink)
console.log(red) //{name:'pink老师',age:18}
red.name = 'red老师'
console.log(red) //{name:'red老师',age:18}
console.log(pink) //{name:'pink老师',age:18}

如果是简单数据类型拷贝值,引用数据类型拷贝的是地址

简单理解: 如果是单层对象,没问题,如果有多层就有问题

eg:

family:{
    mother:'pink妈妈'
}

2.深拷贝

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

常见方法:

  1. 通过 递归实现深 拷贝

  2. lodash cloneDeep

  3. 通过 JSON.stringify 实现

函数递归

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

由于递归很容易发生“栈溢出”错误( stack overflow ),所以必须要加退出条件 return

eg:

let num=1
function fn() {
    console.log()
    if(num>=6){
        return
    }
    num++
    fn()
}
fn()

案例

利用递归函数实现 setTimeout 模拟 setInterval 效果

需求:

①:页面每隔一秒输出当前的时间

②:输出当前时间可以使用: new Date(). toLocaleString ()

function getTime() {
      const time = new Date().toLocaleDateString()
      console.log(time)
      setTimeout(getTime, 1000)
    }
    getTime()

通过递归函数实现深拷贝

eg:

const o = {}
function deepCopy(new,old){
    for(let k in old){
        new[k]=old[k]
    }
}
deepCopy(o,obj)
js库lodash里面cloneDeep内部实现深拷贝

语法:

_.cloneDeep(要被克隆的对象)

eg:

const obj = {
      uname: 'pink',
      age: 18,
      hobby: ['篮球', '足球'],
      family: {
        baby: '小pink'
      }
    }
    const o = _.cloneDeep(obj)
    console.log(o)//[uname: 'pink',age: 18,hobby: ['篮球', '足球'],family: {baby: '小pink'}
    o.family.baby = '老pink'
    console.log(obj)//[uname: 'pink',age: 18,hobby: ['篮球', '足球'],family: {baby: '小pink'}
通过 JSON.stringify 实现

语法:

JSON.parse(JSON.stringify(要被克隆的对象))

eg:

const obj = {
      uname: 'pink',
      age: 18,
      hobby: ['篮球', '足球'],
      family: {
        baby: '小pink'
      }
    }
const o =JSON.parse(JSON.stringify(obj))
...

二、异常处理

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

1.throw抛异常

eg:

function counter(x,y){
    if(!x || !y){
        throw mew Error('参数不能为空!')
    }
    return x+y
}
counter()
  1. throw 抛出异常信息,程序也会终止执行

  2. throw 后面跟的是错误提示信息

  3. Error 对象配合 throw 使用,能够设置更详细的错误信息

2.try/catch 捕获异常

eg:

function foo(){
      try{
        const p = document.querySelector('.p')
        p.style.color= 'red'
      }catch(error){
        console.log(error.message)
        return 
      }
      finally{
        alert('执行')
      }
      console.log('如果出现错误,我的语句不会执行')
    }
    foo()
  1. try...catch 用于捕获错误信息

  2. 将预估可能发生错误的代码写在 try 代码段中

  3. 如果 try 代码段中出现错误后,会执行 catch 代码段,并截获到 错误信息

4.finally 不管是否有错误,都会执行

3.debugger

在代码块里面直接添加debugger,类似与断点调试,借助于浏览器提供的错误信息

三、处理this

this指向

1.普通函数——this指向

普通函数的调用方式决定了 this 的值,即 【 谁调用 this 的值指向谁 】

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

eg:

function sayHi() {
      console.log(this)
    }
    const sayHello = function () {
      console.log(this)
    }
    sayHi() //window
    window.sayHi() //window
    sayHello() //window
'use strict'
    function fn() {
      console.log(this)
    }
    fn() //undefined
2.箭头函数——this指向

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

1.箭头函数会默认帮我们绑定外层 this 的值,所以在箭头函数中 this 的值和外层的 this 是一样的

2.箭头函数中的 this 引用的就是最近作用域中的 this

3.向外层作用域中,一层一层查找 this,直到有 this 的定义

即this为上一层this的定义

eg:

console.log(this) //window

const sayHi=function(){
    console.log(this) // window
}
const user = {
      name: 'xiaom',
      walk: () => {
        console.log(this)
      }
    }
    user.walk() //window

注意

在开发中 【 使用箭头函数前需要考虑函数中 this 的值 】 ,事件回调函数使用箭头函数时 this 为全局的 window

因此DOM 事件回调函 数 如果里面需要 DOM 对象的 this ,则不 推荐使用箭头函数

image.png


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

image.png

总结

不适用构造 函数, 原型函数,dom事件函数等等

适用需要使用上层this 的地方,其余尽量不要在箭头函数内使用到this

改变this

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

1.call()——了解

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

语法:

fun(函数名).call(thisArg,arg1,arg2,...)

  • thisArg:在fun函数运行时指定的this值

  • arg1,arg2:传递的其他参数

  • 返回值就是函数的返回值,因为其是在调用函数

eg:

const obj = {
      name: 'pink'
    }
    function fn(x, y) {
      console.log(this) //指向obj {name:'pink'}
      console.log(x + y)
    }
    fn.call(obj, 1, 2)
2.apply()——理解

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

语法:

fun(函数名).apply(thisArg,[argsArray])

  • thisArg:在fun函数运行时指定的this值

  • argsArray:传递的值,必须包含在数组里面

  • 返回值就是函数的返回值,因为其是在调用函数

  • apply与数组有关系,eg使用Math.max()求数组的最大值

eg:

function counter(x,y){
    return x+y
}
let result = counter.apply(null,[5,10])
console.log(result)
//求数组最大值
const arr=[3,5,2,9]
console.log(Math.max.apply(null,arr)) //9
3.bind()——重点

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

语法:

fun(函数名).bind(thisArg,arg1,arg2,...)

  • thisArg:在fun函数运行时指定的this值

  • arg1,arg2:传递的其他参数

  • 返回由指定的this值和初始化参数改造的原函数拷贝(新函数)

  • 当我们只想改变this指向,并且不想调用这个函数的时候,可以使用bind,eg改变定时器内部的this指向

eg:

function sayHi() {
      console.log(this)
    }
    let user = {
      name: 'xiaom',
      age: 18
    }
    let sayHello = sayHi.bind(user) //this指向user
    sayHello()

image.png

四、性能优化

1.防抖

防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间

eg:搜索框输入,设定每次输入完毕 n 秒后发送请求,如果期间还有输入,则从新计算时间

案例

利用防抖来处理-鼠标滑过盒子显示文字

鼠标在盒子上移动,鼠标停止之后, 500ms 后里面的数字就会变化 +1

利用防抖的方式实现

利用定时器实现,当鼠标滑过,判断有没有定时器,还有就清除,以最后一次滑动为准开启定时器

const box = document.querySelector('.box')
    let i = 1
    function mouseMove() {
      box.innerHTML = i++
    }
    function debounce(fn, t = 500) {
      let timeId
      return function () {
        if (timeId) clearTimeout(timeId)
        timeId = setTimeout(function () {
          fn()
        }, t)
      }
    }
    box.addEventListener('mousemove', debounce(mouseMove, 500))

2.节流

节流,就是指连续触发事件但是在 n 秒中只执行一次函数

在轮播图点击效果、鼠标移动、页面尺寸缩放 resize 、滚动条滚动可以加节流

eg:当一张轮播图完成切换需要300ms,不加节流效果,快速点击,会迅速切换,而添加节流效果,不管快速点击多少次,300ms内,只能切换一张图片

案例

利用节流来处理鼠标滑过盒子显示文字

鼠标在盒子上移动,里面的数字就会变化 +1

利用节流的方式,鼠标经过, 500ms ,数字才显示

利用时间相减:移动后的时间 - 刚开始移动的时间 >= 500ms ,才去执行 mouseMove 函数

const box = document.querySelector('.box')
    let i = 1
    function mouseMove() {
      box.innerHTML = i++
    }
    function throttle(fn, t = 500) {
      let startTime = 0
      return function () {
        let now = Date.now()
        if (now - startTime >= t) {
          fn()
          startTime = now
        }
      }
    }
    box.addEventListener('mousemove', throttle(mouseMove, 500))

也可使用Lodash库实现节流和防抖

节流:

box.addEventListener('mousemove', _.throttle(mouseMove, 500))

防抖:

 box.addEventListener('mousemove', _.debounce(mouseMove, 500))