0x01 深浅拷贝

  • 开发中经常需要拷贝(复制)一个对象,如果直接赋值,则对拷贝对象的修改会影响到源对象

    const o1 = {
    a: 1,
    b: 2
    }
    const o2 = o1
    console.log(o2) // { a: 1, b: 2 } o2.a = 3
    console.log(o1) // { a: 3, b: 2 }
    console.log(o2) // { a: 3, b: 2 }

    原因在于,直接赋值的方法是在拷贝对象数据在栈中的地址,即两个变量操作同一个位置的数据

  • 深拷贝与浅拷贝只针对引用类型

(1)浅拷贝

  • 浅拷贝只将对象或数组的第一层进行复制,其他层级复制的是所存储的内存地址

a. 对象

  • 方法:Object.assign(to, from){...obj}

  • 案例:

    const o1 = {
    a: 1,
    b: 2
    } const o2 = {}
    Object.assign(o2, o1)
    console.log(o2) // { a: 1, b: 2 }
    o2.a = 3
    console.log(o1) // { a: 1, b: 2 }
    console.log(o2) // { a: 3, b: 2 } const o3 = {...o1}
    console.log(o3) // { a: 1, b: 2 }
  • 浅拷贝仅拷贝第一层数据,而不会深入

    const o1 = {
    a: 1,
    b: {
    c: 2
    }
    } const o2 = {}
    Object.assign(o2, o1) o2.b.c = 3 console.log(o1) // { a: 1, b: { c: 3 } }
    console.log(o2) // { a: 1, b: { c: 3 } }

b. 数组

  • 方法:Array.prototype.concat()[...array]

  • 案例:

    const a1 = [1, 2, 3]
    
    const a2 = a1.concat([])
    console.log(a2) // [ 1, 2, 3 ] const a3 = [...a1]
    console.log(a3) // [ 1, 2, 3 ]

(2)深拷贝

  • 深拷贝会构造一个新的复合数组或对象,遇到引用所指向的引用数据类型会继续执行拷贝

  • 常见方法:

a. 递归方法

  • 递归应用举例:通过 setTimeout 模拟 setInterval 效果实现页面中的时钟每秒刷新

    document.body.appendChild(document.createElement('div'))
    function getTime() {
    document.querySelector('div').innerHTML = new Date().toLocaleString()
    setTimeout(getTime, 1000)
    }
    getTime()
  • 基于递归方法的深拷贝案例:

    const o1 = {
    a: 1,
    b: {
    c: 2
    }
    }
    const o2 = {} function deepCopy(newObj, oldObj) {
    // 遍历oldObj中的所有属性
    for (let key in oldObj) {
    // 如果属性值是对象,则递归进行深度复制
    if (typeof oldObj[key] === 'object') {
    newObj[key] = {} // 为newObj创建一个空对象作为属性值
    deepCopy(newObj[key], oldObj[key]) // 递归调用deepCopy函数复制属性值中的所有属性
    } else {
    // 如果属性值不是对象,直接复制
    newObj[key] = oldObj[key]
    }
    }
    } deepCopy(o2, o1)
    console.log(o2) // { a: 1, b: { c: 2 } }
    o2.b.c = 3
    console.log(o1) // { a: 1, b: { c: 2 } }
    console.log(o2) // { a: 1, b: { c: 3 } }

b. Lodash

const o1 = {
a: 1,
b: {
c: 2
}
} const o2 = _.cloneDeep(o1) console.log(o2)

c. JSON 方法

const o1 = {
a: 1,
b: {
c: 2
}
} const o2 = JSON.parse(JSON.stringify(o1)) console.log(o2)

0x02 异常处理

  • 定义:预估代码运行过程中可能发生的错误,使用特定的方法对这些错误进行合适的处理
  • 意义:有助于提高代码健壮性

(1)throw 抛出异常

function division(x, y) {
if(!x || !y) {
throw "The parameter cannot be empty"
}
if(y === 0) {
throw new Error("Divisor cannot be zero")
}
}
division()
  • throw 抛出异常信息,程序也会中止
  • Error 对象常配合 throw 使用,能够设置更详细的错误消息

(2)try...catch 捕获异常

document.body.appendChild(document.createElement('p'))
function fun() {
try {
// Correct: document.querySelector('p').style.color = 'red'
document.querySelector('.p').style.color = 'red'
} catch (e) {
console.log("Catch a error: ", e.message)
} finally {
console.log("Finally")
}
}
fun()
  • 用于捕获错误信息
  • try 中写入可能会发生错误的代码
  • catch 中写入捕获错误后的处理
  • finally 中写入无论是否出错都会执行的代码

(3)debugger

const btn = document.createElement('button')
btn.onclick = function () {
debugger
}
btn.textContent = 'Debug'
document.body.appendChild(btn)
  • 使用 debugger 可以进入逐步调试

0x03 处理 this

(1)this 指向

a. 普通函数

  • 普通函数的调用方式决定了 this 的值

    const obj = {
    a: function fun() {
    console.log(this);
    }
    }
    obj.a() // {a:f}
  • 没有明确的调用方法时,this 的值为 window

    function fun() {
    console.log(this) // [object Window]
    }
    fun()
  • 严格模式下没有明确的调用方法时,this 的值为 undefined

    'use strict'
    function fun() {
    console.log(this) // undefined
    }
    fun()

b. 箭头函数

  • 箭头函数不存在 this

    • 箭头函数会默认绑定外层 this 的值
    • 箭头函数中的 this 引用的是最近作用域中的 this
    • 箭头函数会向外层作用域中一层层查找 this,直至找到有 this 的定义
    const obj = {
    a: () => {
    console.log(this)
    }
    }
    obj.a() // [object Window]
  • 在开发中,使用箭头函数前需要考虑函数中 this 的值

    const btn = document.createElement("button")
    btn.textContent = "Click"
    btn.addEventListener("click", function() {
    console.log(this) // <button>Click</button>
    })
    btn.addEventListener("click", () => {
    console.log(this) // [object Window]
    })
    document.body.appendChild(btn)
  • 基于原型的面向对象不推荐采用箭头函数

    function Obj() {}
    Obj.prototype.a = () => {
    console.log(this)
    }
    const obj = new Obj()
    obj.a() // [object Window]

(2)改变 this

  • 有三个方法可以动态指定普通函数中 this 的指向

a. call()

  • 语法:call(thisArg, arg1, arg2, ...)

    • thisArg:在函数运行时,指定 this 的值
    • arg1, arg2, ...:传参
    • 返回值就是函数的返回值
  • 举例

    const obj = { a: 0 }
    function fun(x, y) {
    console.log(x, y, this) // 1 2 {a: 0}
    }
    fun.call(obj, 1, 2)

b. apply()

  • 语法:fun.apply(thisArg, [argsArray])

    • thisArg:在函数运行时,指定 this 的值
    • argsArray:传参,必须包含在数组里面
    • 返回值就是函数的返回值
    • 因此 apply 主要跟数组有关系
  • 举例

    function sum(x, y) {
    console.log(this) // [object Window]
    return x + y
    }
    console.log(sum.apply(null, [1, 2])) // 3
    console.log(Math.max.apply(Math, [1, 2, 3])) // 3

c. bind()

  • bind 方法不会调用函数,但是也可以改变函数内部的 this 指向

  • 语法:bind(thisArg, arg1, arg2, ...)

    • thisArg:在函数运行时,指定 this 的值
    • arg1, arg2, ...:传参,必须包含在数组里面
    • 返回值由指定的 this 值和初始化参数改造的原函数拷贝
  • 举例

    const obj = { a: 0 }
    function fun(x, y) {
    console.log(x, y, this)
    }
    fun.bind(obj, 1, 2)() // 1 2 {a: 0}

0x04 性能优化

(1)防抖

  • 防抖(debounce):单位时间内,频繁触发事件,只执行最后一次

    • 触发事件后,在 \(n\) 秒内函数只能执行一次,如果在 \(n\) 秒内再次被触发,则重新计算函数执行时间
  • 使用场景:常用于输入事件的处理中,以减少不必要的计算或操作

  • 举例:鼠标在盒子上移动,每 500ms 盒内数字加一

    const box = document.createElement('div')
    document.body.appendChild(box) let cnt = 1
    function add() {
    box.innerHTML = cnt++
    } function debounce(func, timeMs) {
    let timer
    return function () {
    if (timer) clearTimeout(timer)
    timer = setTimeout(function () {
    func()
    }, timeMs)
    }
    } box.addEventListener("mousemove", debounce(add, 500))
  • 防抖函数的封装说明

    /**
    * @param {Function} func 要执行的函数。
    * @param {number} timeMs 延迟的时间,单位为毫秒。
    * @returns {Function} 返回一个新的函数,该函数具有防抖功能。
    */
    function debounce(func, timeMs) {
    let timer // 用于存储定时器的变量 // 返回一个新的函数,该函数会延迟执行传入的func函数
    return function () {
    if (timer) clearTimeout(timer) // 如果存在定时器,则清除,以防止之前设定的执行被触发 // 设定一个新的定时器,当延迟时间过去后,执行func函数
    timer = setTimeout(function () {
    func()
    }, timeMs)
    }
    }

(2)节流

  • 节流(throttle):单位时间内,频繁触发事件,只执行一次

    • 连续触发事件,但在 \(n\) 秒内仅执行一次函数
  • 使用场景:常用于高频事件的处理中,以减少不必要的性能消耗

  • 举例:鼠标在盒子上移动,每 500ms 盒内数字加一

    const box = document.createElement('div')
    document.body.appendChild(box) let cnt = 1
    function add() {
    box.innerHTML = cnt++
    } function throttle(func, timeMs) {
    let timer = null
    return function () {
    if (!timer) {
    timer = setTimeout(function() {
    func()
    timer = null
    }, timeMs)
    }
    }
    } box.addEventListener("mousemove", throttle(add, 500))
  • 节流函数封装说明

    /**
    * @param {Function} func 要节流的函数
    * @param {number} timeMs 节流的时间间隔(毫秒)
    * @returns {Function} 返回一个新函数,新函数将控制原函数在指定时间间隔内只执行一次
    */
    function throttle(func, timeMs) {
    let timer = null // 利用闭包保存一个定时器变量 return function () {
    // 如果定时器不存在,则设置定时器
    if (!timer) {
    timer = setTimeout(function() {
    func() // 在指定时间间隔后执行原函数
    timer = null // 执行后重置定时器变量
    }, timeMs)
    }
    }
    }

-End-

JavaScript 高阶技巧的更多相关文章

  1. 现代 CSS 高阶技巧,完美的波浪进度条效果!

    本文是 CSS Houdini 之 CSS Painting API 系列第三篇. 现代 CSS 之高阶图片渐隐消失术 现代 CSS 高阶技巧,像 Canvas 一样自由绘图构建样式! 在上两篇中,我 ...

  2. 现代 CSS 高阶技巧,不规则边框解决方案

    本文是 CSS Houdini 之 CSS Painting API 系列第四篇. 现代 CSS 之高阶图片渐隐消失术 现代 CSS 高阶技巧,像 Canvas 一样自由绘图构建样式! 现代 CSS ...

  3. JavaScript高阶函数之filter、map、reduce

    JavaScript高阶函数 filter(过滤) 用法: 用于过滤,就是把数组中的每个元素,使用回调函数func进行校验,回调函数func返回一个布尔值,将返回值为 true 的元素放入新数组 参数 ...

  4. JavaScript高阶函数

    所谓高阶函数(higher-order function) 就是操作函数的函数,它接收一个或多个函数作为参数,并返回一个新函数. 下面的例子接收两个函数f()和g(),并返回一个新的函数用以计算f(g ...

  5. JavaScript高阶函数 map reduce filter sort

    本文是笔者在看廖雪峰老师JavaScript教程时的个人总结 高阶函数            一个函数就接收另一个函数作为参数,这种函数就称之为高阶函数          1.高阶函数之map:   ...

  6. JavaScript高阶函数的应用

    定义 高阶函数是指至少满足下列条件之一的函数: 函数可以作为参数被传递: 函数可以作为返回值输出. JavaScript语言中的函数显然满足高阶函数的条件,在实际开发中,无论是将函数当作参数传递,还是 ...

  7. JavaScript 高阶函数 + generator生成器

    map/reduce map()方法定义在JavaScript的Array中,我们调用Array的map()方法,传入我们自己的函数,就得到了一个新的Array作为结果: function pow(x ...

  8. JavaScript 高阶函数

    高阶函数的英文叫Higher-order function ,什么是高阶函数呢>? JavaScript的函数其实都指向某个变量.既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接 ...

  9. 浅析javascript高阶函数

    什么是高阶函数:在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数: 1. 接受一个或多个函数作为输入: 2. 输出一个函数.在数学中它们也叫做算子(运算符)或泛函.微积分中的导数就是常见的例 ...

  10. JavaScript高阶函数map/reduce、filter和sort

    map() 举例说明,比如我们有一个函数f(x)=x²,要把这个函数作用在一个数组[1,2,3,4,5,6,7,8,9]上. 由于map()方法定义在JavaScript的Array中,我们调用Arr ...

随机推荐

  1. 解密Spring中的Bean实例化:推断构造方法(上)

    在Spring中,一个bean需要通过实例化来获取一个对象,而实例化的过程涉及到构造方法的调用.本文将主要探讨简单的构造推断和实例化过程,让我们首先深入了解实例化的步骤. 实例化源码 protecte ...

  2. Java 设计模式----单例模式--饿汉式

    1 package com.bytezreo.singleton; 2 3 /** 4 * 5 * @Description 单例设计模式 例子-----饿汉式 6 * @author Bytezer ...

  3. C++ STL //vector容器存放内置数组

    1 //STL初始 2 // 3 //vector容器存放内置数组 4 5 #include <iostream> 6 #include <string> 7 #include ...

  4. 我见过最好的.NET/C#图片工具(裁剪、缩放、与加水印)

    付费才能得到的好资源,限今天"免费"领取,月薪超30k必备技能! 资源1:高薪热门[WPF上位机+工业互联网]从零手写实战回复wpf免费领取 资源2:C#+Halcon机器视觉零基 ...

  5. aardio用udp获取最佳本机IP地址

    此方法在有多个网络接口的时候,例如部分虚拟网卡的情况,获取最合适的本地ip. 用UDP连接虚假IP地址以获取返回的本机IP import wsock.udp.client; import consol ...

  6. 本地画板工具 Axure RP 9 顶替 drawio (补充Axure RP 8)

    本地画板工具 Axure RP 9 顶替 drawio 外链:https://wws.lanzoul.com/b03paemkf 密码:dmvj 9这个版本 win7 不支持 Axure RP 8 h ...

  7. 删除文件或目录 被进程占用或锁定locked 查询进程 资源监视器-cpu-关联句柄-输入文件全路径

    删除文件或目录 被进程占用或锁定 查询进程 资源监视器-cpu-关联句柄-输入文件全路径 右键点击桌面的Win图标,点击"任务管理器">>点击左上角"性能&q ...

  8. 火柴 基于everything的搜索软件 软件推荐 Ctrl+Ctrl 显示 tab转换 本机搜索和网络搜索

    https://www.huochaipro.com/

  9. DRC音频处理算法原理解析及仿真结果

    一 概念: 在声学领域中,DRC(Dynamic range compression) 一般用来动态调整音频输出幅值,在音量大时压制音量在某一范围内,在音量小时适当提升音量.通常用于控制音频输出功率, ...

  10. 优雅的处理挂载window上的函数可能不存在的情况

    背景 在做一个Web JS SDK(A)时,内部会用到另一个Web JS SDK(B)的方法.(文中后续用A/B代替两者) B通常会提供Script和NPM包两种使用方式 使用npm pkg的缺点 增 ...