大家有关于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
常见方法:
1. 通过递归实现深拷贝
函数递归:
如果一个函数在内部可以调用其本身,那么这个函数就是递归函数
练习:
利用递归函数实现 setTimeout 模拟 setInterval效果
需求:
①:页面每隔一秒输出当前的时间
②:输出当前时间可以使用:new Date().toLocaleString()
常见方法:
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()实现
总结
实现深拷贝三种方式?
自己利用递归函数书写深拷贝
利用js库 lodash里面的 _.cloneDeep()
利用JSON字符串转换
异常处理
throw 抛异常
异常处理是指预估代码执行过程中可能发生的错误,然后最大程度的避免错误的发生导致整个程序无法继续运行
总结:
1. throw 抛出异常信息,程序也会终止执行
2. throw 后面跟的是错误提示信息
3. Error 对象配合 throw 使用,能够设置更详细的错误信息
• try /catch 捕获异常
我们可以通过try / catch 捕获错误信息(浏览器提供的错误信息) try 试试 catch 拦住 finally 最后
总结:
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
命令留在你的代码中,因为它会干扰应用的正常运行。
处理this
this指向
this 是 JavaScript 最具“魅惑”的知识点,不同的应用场合 this 的取值可能会有意想不到的结果,在此我们对以往学习
过的关于【 this 默认的取值】情况进行归纳和总结。
目标: 了解函数中 this 在不同场景下的默认值,知道动态指定函数 this 值的方法
this
是 JavaScript 中的一个关键字,它代表当前函数执行的上下文对象。this
的值在不同的情况下会有不同的指向。
以下是一些常见的 this
指向:
-
全局作用域下的
this
指向全局对象window
。 -
在函数中,
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 对象
-
在构造函数中,
this
的值指向当前实例对象。function Person(name) {this.name = name;this.sayName = function() {console.log(this.name);} }var john = new Person('John'); john.sayName(); // this 指向 john 实例对象
-
在事件处理函数中,
this
的值指向触发事件的元素。<button id="myButton">Click Me!</button> <script>var button = document.getElementById("myButton");button.onclick = function() {console.log(this); // this 指向触发事件的元素,即 button 元素} </script>
-
在箭头函数中,
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 会临时将其转换为一个对象,并且 null
和 undefined
会被转换为全局对象。因此,在使用时需要注意传入的参数是否符合要求。
箭头函数中的 this 与普通函数完全不同,也不受调用方式的影响,事实上箭头函数中并不存在 this !
1. 箭头函数会默认帮我们绑定外层 this 的值,所以在箭头函数中 this 的值和外层的 this 是一样的
2.箭头函数中的this引用的就是最近作用域中的this
3.向外层作用域中,一层一层查找this,直到有this的定义
注意情况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
的值,使用方法如下代码所示:
<script>// 普通函数function sayHi() {console.log(this);} let user = {name: '小明',age: 18} let student = {name: '小红',age: 16} // 调用函数并指定 this 的值sayHi.call(user); // this 值为 usersayHi.call(student); // this 值为 student // 求和函数function counter(x, y) {return x + y;} // 调用 counter 函数,并传入参数let result = counter.call(null, 5, 10);console.log(result); </script>
总结:
-
call
方法能够在调用函数的同时指定this
的值 -
使用
call
方法调用函数时,第1个参数为this
指定的值 -
call
方法的其余参数会依次自动传入函数做为函数的参数
apply
使用 call
方法调用函数,同时指定函数中 this
的值,使用方法如下代码所示:
<script>// 普通函数function sayHi() {console.log(this)} let user = {name: '小明',age: 18} let student = {name: '小红',age: 16} // 调用函数并指定 this 的值sayHi.apply(user) // this 值为 usersayHi.apply(student) // this 值为 student // 求和函数function counter(x, y) {return x + y}// 调用 counter 函数,并传入参数let result = counter.apply(null, [5, 10])console.log(result) </script>
总结:
-
apply
方法能够在调用函数的同时指定this
的值 -
使用
apply
方法调用函数时,第1个参数为this
指定的值 -
apply
方法第2个参数为数组,数组的单元值依次自动传入函数做为函数的参数
bind
bind
方法并不会调用函数,而是创建一个指定了 this
值的新函数,使用方法如下代码所示:
<script>// 普通函数function sayHi() {console.log(this)}let user = {name: '小明',age: 18}// 调用 bind 指定 this 的值let sayHello = sayHi.bind(user);// 调用使用 bind 创建的新函数sayHello() </script>
注:bind
方法创建新的函数,与原函数的唯一的变化是改变了 this
的值。
性能优化
节流(throttle)
所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数
JavaScript性能优化有许多方法,以下是一些常见的技巧:
-
减少重绘和回流:重绘和回流会导致页面的重新布局和渲染,会影响性能。可以通过减少对DOM的查询、使用documentFragment、使用CSS3动画等方式来减少重绘和回流。
-
减少HTTP请求:减少页面资源的请求可以大大提高页面的加载速度。可以通过合并CSS和JavaScript文件、使用图片的雪碧图等方式减少HTTP请求。
-
使用缓存:可以使用浏览器的缓存机制来减少请求和提高加载速度。可以使用浏览器缓存或者将静态资源放在CDN上。
-
避免不必要的计算:在编写JavaScript代码时,应避免不必要的计算。可以使用缓存和预处理等技术来避免重复的计算。
-
使用事件委托:事件委托可以减少事件处理器的数量,从而提高性能。可以将事件处理器绑定在父元素上,通过事件冒泡机制来处理子元素的事件。
-
使用异步加载:可以使用异步加载来提高页面加载速度。可以使用异步请求、defer和async等方式来实现异步加载。
-
关闭循环引用:JavaScript中的循环引用会导致内存泄漏,可以使用垃圾回收器来回收不再使用的内存。
-
避免过度渲染:在使用JavaScript更新DOM时,应避免过度渲染。可以使用requestAnimationFrame来避免过度渲染。
节流(throttle)
所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数
开发使用场景 – 小米轮播图点击效果 、 鼠标移动、页面尺寸缩放resize、滚动条滚动 就可以加节流
假如一张轮播图完成切换需要300ms, 不加节流效果,快速点击,则嗖嗖嗖的切换
加上节流效果, 不管快速点击多少次, 300ms时间内,只能切换一张图片。
js防抖和节流的区别
防抖和节流都是为了优化JavaScript函数的性能,但两者的实现和目的略有不同。
- 防抖 (Debounce)
防抖是指在一定时间内,多次触发同一事件,只执行最后一次操作,也就是抖动结束后才执行。例如在输入框输入搜索内容时,在用户连续输入时不会立即进行搜索,而是等待用户停止输入一段时间后再进行搜索。防抖可以减少函数的执行次数,避免频繁的操作。
实现方式:每次触发事件时都会清除之前的定时器,重新设置一个新的定时器。当事件停止触发一定时间后,定时器执行函数。
- 节流 (Throttle)
节流是指一定时间内,多次触发同一事件,只执行一次操作。例如在拖拽一个元素时,需要不断获取其位置,但不应该每次都进行计算,而是一段时间内只计算一次并返回计算结果。节流可以控制函数的执行频率。
实现方式:通过设置一个定时器,在一定时间内只执行一次函数。如果定时器还未执行,再次触发事件则不执行函数。
区别:
- 防抖是将多个操作合并成一个操作,等待用户停止操作后再执行,节流是一定时间内只执行一次操作。
- 防抖是在用户停止操作后执行一次,节流是在一定时间结束后执行一次。
- 防抖适用于用户不断触发事件后,只需执行一次操作的情况,如按钮点击、输入框搜索等;节流适用于用户连续触发事件后,需要等待一定时间后才进行操作的情况。
以上就是全部的关于JavaScript知识点以及案例 (有需要素材的及时联系我!!)