深入探讨JavaScript高阶编程技巧:突破技能瓶颈的高级实践

 大家有关于JavaScript知识点不知道可以去

博客主页:阿猫的故乡

系列专栏:JavaScript专题栏

欢迎关注:点赞收藏✍️留言

目录

学习目标:

学习内容:

学习时间:

学习知识:

深浅拷贝:

浅拷贝

深拷贝

 练习:

总结 

异常处理

throw 抛异常

• try /catch 捕获异常

• debugger

处理this

this指向

• 改变this

改变this指向

call

apply

bind

性能优化

 js防抖和节流的区别


学习目标:

深入this学习,知道如何判断this指向和改变this指向
知道在JS中如何处理异常,学习深浅拷贝,理解递归

 


学习内容:

深浅拷贝
异常处理
处理this
性能优化
综合案例

 


学习时间:

提示:这里可以添加计划学习的时间

例如:

  • 周一至周五晚上 7 点—晚上9点
  • 周六上午 9 点-上午 11 点
  • 周日下午 3 点-下午 6 点

学习知识:

深浅拷贝:

浅拷贝

开发中我们经常需要复制一个对象。如果直接用赋值会有下面问题:

使用浅拷贝的注意点:

  • 如果原始对象的属性值是一个对象或数组,那么拷贝得到的是该对象或数组的引用,而不是复制一个新的对象或数组。
  • 如果原始对象的属性值是函数,浅拷贝只是将函数的引用拷贝过去,而不是函数的实现代码。

常见方法:
1. 拷贝对象:Object.assgin() / 展开运算符 {...obj} 拷贝对象
2.拷贝数组:Array.prototype.concat() 或者 [...arr]
直接赋值和浅拷贝有什么区别?
 直接赋值的方法,只要是对象,都会相互影响,因为是直接拷贝对
象栈里面的地址
 浅拷贝如果是一层对象,不相互影响,如果出现多层对象拷贝还会
相互影响
2. 浅拷贝怎么理解?
 拷贝对象之后,里面的属性值是简单数据类型直接拷贝值
 如果属性值是引用数据类型则拷贝的是地址
 


深拷贝

首先浅拷贝和深拷贝只针对引用类型
深拷贝:拷贝的是对象,不是地址

常见方法:
1. 通过递归实现深拷贝
2. lodash/cloneDeep
3. 通过JSON.stringify()实现
 常见方法:
1. 通过递归实现深拷贝
函数递归:
如果一个函数在内部可以调用其本身,那么这个函数就是递归函数

  •  简单理解:函数内部自己调用自己, 这个函数就是递归函数
  •  递归函数的作用和循环效果类似
  •  由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件 return

 深入探讨JavaScript高阶编程技巧:突破技能瓶颈的高级实践_第1张图片

常见方法:
1. 通过递归实现深拷贝

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

深入探讨JavaScript高阶编程技巧:突破技能瓶颈的高级实践_第2张图片

 练习:

利用递归函数实现 setTimeout 模拟 setInterval效果
需求:
①:页面每隔一秒输出当前的时间
②:输出当前时间可以使用:new Date().toLocaleString() 
 

深入探讨JavaScript高阶编程技巧:突破技能瓶颈的高级实践_第3张图片

常见方法:
1. 通过递归函数实现深拷贝(简版)
 

深拷贝是指将一个对象复制到一个新对象中,并且新对象中所有的属性的值都是原对象中对应属性的复制,而且如果属性的值是对象类型,则对该对象也进行深拷贝,使得新对象与原对象完全独立。

要实现深拷贝,可以使用递归函数。递归函数是指在函数内调用自身的函数。使用递归函数可以遍历对象的所有属性,并将它们复制到新对象中。

下面是一个使用递归函数实现深拷贝的例子:

function deepCopy(obj) {
  // 如果是简单类型或者 null,则直接返回
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }
  
  // 创建一个新对象
  const newObj = Array.isArray(obj) ? [] : {};
  
  // 遍历对象的所有属性
  for (let key in obj) {
    // 如果属性是对象类型,则递归调用该函数
    if (typeof obj[key] === 'object') {
      newObj[key] = deepCopy(obj[key]);
    } else {
      // 如果是简单类型,则直接复制值
      newObj[key] = obj[key];
    }
  }
  
  // 返回新对象
  return newObj;
}

这个函数接受一个对象作为参数,并返回一个它的深度拷贝。如果参数是一个简单类型或者 null,则直接返回它本身。否则,创建一个新对象,遍历原对象的所有属性,如果属性是对象类型,则递归调用该函数,否则直接复制属性的值。最后返回新对象。

常见方法:
3. 通过JSON.stringify()实现
深入探讨JavaScript高阶编程技巧:突破技能瓶颈的高级实践_第4张图片

总结 

实现深拷贝三种方式?
自己利用递归函数书写深拷贝
利用js库 lodash里面的 _.cloneDeep() 
利用JSON字符串转换

异常处理

throw 抛异常

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

深入探讨JavaScript高阶编程技巧:突破技能瓶颈的高级实践_第5张图片
总结:
1. throw 抛出异常信息,程序也会终止执行
2. throw 后面跟的是错误提示信息
3. Error 对象配合 throw 使用,能够设置更详细的错误信息


• try /catch 捕获异常

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

深入探讨JavaScript高阶编程技巧:突破技能瓶颈的高级实践_第6张图片
1. try...catch 用于捕获错误信息
2. 将预估可能发生错误的代码写在 try 代码段中
3. 如果 try 代码段中出现错误后,会执行 catch 代码段,并截获到错误信息
4. finally 不管是否有错误,都会执行

在 JavaScript 中,try/catch 语句用于捕获和处理代码执行期间抛出的异常(或错误)。try 块中的代码可能会抛出一个异常,如果这种情况发生,则异常会被传递到 catch 块中进行处理。

以下是使用 try/catch 语句的基本语法:

try {
  // 可能会抛出异常的代码
} catch (e) {
  // 异常处理逻辑
}

当 try 块中的代码抛出异常时,catch 块中的代码将被执行。catch 块中的参数 e 是一个异常对象,它包含有关异常的信息(例如错误消息)。

在 catch 块中,您可以执行任何适当的操作来处理异常。例如,您可以使用 console.log() 语句将错误消息写入控制台,或显示一个警报框来向用户指示发生了错误。

以下是一个简单的示例,演示如何使用 try/catch 块来捕获和处理异常:

try {
  // 这行代码会抛出一个异常
  undefinedFunction();
} catch (e) {
  // 异常处理逻辑
  console.log('发生了一个错误:' + e.message);
}

在此示例中,try 块中的代码会调用一个未定义的函数 undefinedFunction(),这将抛出一个异常。由于try/catch 块存在,异常被捕获并传递给 catch 块中的代码进行处理。在这个示例中,catch 块中的代码使用 console.log() 语句将错误消息写入控制台。


• debugger

debugger 是 JavaScript 的内置调试器命令,它可以让你在代码中设置断点,以便在运行时暂停代码执行并检查程序状态。

在代码中插入 debugger 命令会启用调试器,并在执行到这个地方时暂停代码的执行。此时你可以在调试器中进行检查和调试。

以下是一个简单的示例:

function calculate(x, y) {
  debugger;
  return x + y;
}

var result = calculate(2, 3);
console.log(result);

在此示例中,我们创建了一个名为 calculate 的函数,它接受两个参数并返回它们的和。在函数中插入了 debugger 命令,这意味着在代码执行到这里时会暂停。然后我们调用 calculate 函数,并将返回值存储在 result 变量中。最后,我们在控制台中输出 result 的值。

当你在浏览器中执行此代码时,浏览器将在执行到 debugger 命令时自动启用调试器并暂停代码执行。在调试器中,你可以检查变量的值、执行代码段、单步运行代码以及检查调用栈等等。

请注意,在发布/生产环境中不应该将 debugger 命令留在你的代码中,因为它会干扰应用的正常运行。

深入探讨JavaScript高阶编程技巧:突破技能瓶颈的高级实践_第7张图片

处理this

this指向

this 是 JavaScript 最具“魅惑”的知识点,不同的应用场合 this 的取值可能会有意想不到的结果,在此我们对以往学习
过的关于【 this 默认的取值】情况进行归纳和总结。
目标: 了解函数中 this 在不同场景下的默认值,知道动态指定函数 this 值的方法

this 是 JavaScript 中的一个关键字,它代表当前函数执行的上下文对象。this 的值在不同的情况下会有不同的指向。

以下是一些常见的 this 指向:

  1. 全局作用域下的 this 指向全局对象 window

  2. 在函数中,this 的值取决于函数如何被调用,当函数被作为一个普通函数调用时,this 的值指向全局对象 window。但是如果函数作为对象的方法调用时,this 的值指向这个对象。

    function person() {
      console.log(this);
    }
    
    person(); // 在全局作用域下调用,this 指向 window 对象
    
    var obj = {
      name: 'John',
      sayName: function() {
        console.log(this.name);
      }
    };
    
    obj.sayName(); // 作为 obj 对象的方法调用,this 指向 obj 对象
    

  3. 在构造函数中,this 的值指向当前实例对象。

    function Person(name) {
      this.name = name;
      this.sayName = function() {
        console.log(this.name);
      }
    }
    
    var john = new Person('John');
    john.sayName(); // this 指向 john 实例对象
    

  4. 在事件处理函数中,this 的值指向触发事件的元素。

    
    
    

  5. 在箭头函数中,this 的值指向当前上下文对象。

    var obj = {
      name: 'John',
      sayName: function() {
        setTimeout(() => {
          console.log(this.name);
        }, 1000);
      }
    };
    
    obj.sayName(); // this 指向 obj 对象
    

需要注意的是,this 的值是在函数执行的时候才能确定,而不是在函数定义的时候。因此,在使用 this 时需要注意当前函数在什么上下文中被调用。


• 改变this
 

在 JavaScript 中,可以使用 call()apply()bind() 方法来改变函数中 this 的指向。

call()apply() 方法可以直接将要改变的 this 上下文对象指定为函数调用时的第一个参数,接下来的参数是被调用函数的参数,这两个方法的主要区别在于传参的方式不同,call() 方法的参数是一系列的参数列表,而 apply() 方法的参数是一个数组。

function sayName() {
  console.log(this.name);
}

var person1 = {
  name: 'John'
};

var person2 = {
  name: 'Mike'
};

sayName.call(person1); // 输出 'John'
sayName.apply(person2); // 输出 'Mike'

bind() 方法将被调用的函数和要改变的 this 上下文对象绑定,并返回一个新的函数,原始函数中的 this 的指向不会发生改变。

var person1 = {
  name: 'John',
  sayName: function() {
    console.log(this.name);
  }
};

var person2 = {
  name: 'Mike'
};

var sayName2 = person1.sayName.bind(person2); // 绑定 sayName() 函数和 person2 对象
sayName2(); // 输出 'Mike'

需要注意的是,使用 call()apply()bind() 方法改变 this 的指向时,如果传入的 this 参数不是一个对象,JavaScript 会临时将其转换为一个对象,并且 nullundefined 会被转换为全局对象。因此,在使用时需要注意传入的参数是否符合要求。

箭头函数中的 this 与普通函数完全不同,也不受调用方式的影响,事实上箭头函数中并不存在 this
1. 箭头函数会默认帮我们绑定外层 this 的值,所以在箭头函数中 this 的值和外层的 this 是一样的
2.箭头函数中的this引用的就是最近作用域中的this
3.向外层作用域中,一层一层查找this,直到有this的定义
 深入探讨JavaScript高阶编程技巧:突破技能瓶颈的高级实践_第8张图片

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

2. apply()-理解
使用 apply 方法调用函数,同时指定被调用函数中 this 的值
 语法:

fun.apply(thisArg, [argsArray])


 thisArg:在fun函数运行时指定的 this 值
 argsArray:传递的值,必须包含在数组里面
 返回值就是函数的返回值,因为它就是调用函数
 因此 apply 主要跟数组有关系,比如使用 Math.max() 求数组的最大值
fun.apply(thisArg, [argsArray])

改变this指向

以上归纳了普通函数和箭头函数中关于 this 默认值的情形,不仅如此 JavaScript 中还允许指定函数中 this 的指向,有 3 个方法可以动态指定普通函数中 this 的指向:

call

使用 call 方法调用函数,同时指定函数中 this 的值,使用方法如下代码所示:

总结:

  1. call 方法能够在调用函数的同时指定 this 的值

  2. 使用 call 方法调用函数时,第1个参数为 this 指定的值

  3. call 方法的其余参数会依次自动传入函数做为函数的参数

apply

使用 call 方法调用函数,同时指定函数中 this 的值,使用方法如下代码所示:

总结:

  1. apply 方法能够在调用函数的同时指定 this 的值

  2. 使用 apply 方法调用函数时,第1个参数为 this 指定的值

  3. apply 方法第2个参数为数组,数组的单元值依次自动传入函数做为函数的参数

bind

bind 方法并不会调用函数,而是创建一个指定了 this 值的新函数,使用方法如下代码所示:

注:bind 方法创建新的函数,与原函数的唯一的变化是改变了 this 的值。

性能优化

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

JavaScript性能优化有许多方法,以下是一些常见的技巧:

  1. 减少重绘和回流:重绘和回流会导致页面的重新布局和渲染,会影响性能。可以通过减少对DOM的查询、使用documentFragment、使用CSS3动画等方式来减少重绘和回流。

  2. 减少HTTP请求:减少页面资源的请求可以大大提高页面的加载速度。可以通过合并CSS和JavaScript文件、使用图片的雪碧图等方式减少HTTP请求。

  3. 使用缓存:可以使用浏览器的缓存机制来减少请求和提高加载速度。可以使用浏览器缓存或者将静态资源放在CDN上。

  4. 避免不必要的计算:在编写JavaScript代码时,应避免不必要的计算。可以使用缓存和预处理等技术来避免重复的计算。

  5. 使用事件委托:事件委托可以减少事件处理器的数量,从而提高性能。可以将事件处理器绑定在父元素上,通过事件冒泡机制来处理子元素的事件。

  6. 使用异步加载:可以使用异步加载来提高页面加载速度。可以使用异步请求、defer和async等方式来实现异步加载。

  7. 关闭循环引用:JavaScript中的循环引用会导致内存泄漏,可以使用垃圾回收器来回收不再使用的内存。

  8. 避免过度渲染:在使用JavaScript更新DOM时,应避免过度渲染。可以使用requestAnimationFrame来避免过度渲染。

 节流(throttle)
所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数
 开发使用场景 – 小米轮播图点击效果 、 鼠标移动、页面尺寸缩放resize、滚动条滚动 就可以加节流
 假如一张轮播图完成切换需要300ms, 不加节流效果,快速点击,则嗖嗖嗖的切换
 加上节流效果, 不管快速点击多少次, 300ms时间内,只能切换一张图片。

 js防抖和节流的区别

防抖和节流都是为了优化JavaScript函数的性能,但两者的实现和目的略有不同。

  1. 防抖 (Debounce)

防抖是指在一定时间内,多次触发同一事件,只执行最后一次操作,也就是抖动结束后才执行。例如在输入框输入搜索内容时,在用户连续输入时不会立即进行搜索,而是等待用户停止输入一段时间后再进行搜索。防抖可以减少函数的执行次数,避免频繁的操作。

实现方式:每次触发事件时都会清除之前的定时器,重新设置一个新的定时器。当事件停止触发一定时间后,定时器执行函数。

  1. 节流 (Throttle)

节流是指一定时间内,多次触发同一事件,只执行一次操作。例如在拖拽一个元素时,需要不断获取其位置,但不应该每次都进行计算,而是一段时间内只计算一次并返回计算结果。节流可以控制函数的执行频率。

实现方式:通过设置一个定时器,在一定时间内只执行一次函数。如果定时器还未执行,再次触发事件则不执行函数。

区别:

  • 防抖是将多个操作合并成一个操作,等待用户停止操作后再执行,节流是一定时间内只执行一次操作。
  • 防抖是在用户停止操作后执行一次,节流是在一定时间结束后执行一次。
  • 防抖适用于用户不断触发事件后,只需执行一次操作的情况,如按钮点击、输入框搜索等;节流适用于用户连续触发事件后,需要等待一定时间后才进行操作的情况。

以上就是全部的关于JavaScript知识点以及案例 (有需要素材的及时联系我!!)

你可能感兴趣的:(webAPL,前端,JavaScript,javascript,开发语言,ecmascript)