深浅拷贝
- 首先浅拷贝和深拷贝只针对像Object,Array这样的复杂对象,简单来说,浅拷贝只复制一层对象的属性,深拷贝则复制了所有的层级。 对于字符串类型,浅复制是对值的复制,对于对象来说,浅复制是对对象地址的复制,并没有开辟新的栈,也就是复制的结果是两个对象指向同一个同一个地址,修改其中一个对象的属性,则另一个对象的属性也会改变,而深复制则是开辟新的栈,两个对象对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性。
const obj = {
uname: 'pink',
age: 18,
hobby: ['乒乓球', '足球'],
family: {
baby: '小pink'
}
}
// 把对象转换为 JSON 字符串
// console.log(JSON.stringify(obj))
const o = JSON.parse(JSON.stringify(obj))
console.log(o)
o.family.baby = '123'
console.log(obj)
异常处理
throw
异常处理是指代码预估执行过程中可能发生的错误,然后最大程度上的避免错误的发生导致整个程序无法继续运行。
<script>
function counter(x, y) {
if(!x || !y) {
// throw '参数不能为空!';
throw new Error('参数不能为空!')
}
return x + y
}
counter()
</script>
总结:
throw抛出异常信息,程序也会终止执行throw后面跟得是错误提示信息Error对象配合throw使用,能够设置更详细的错误信息
try ... catch
<script>
function foo() {
try {
// 查找 DOM 节点
const p = document.querySelector('.p')
p.style.color = 'red'
} catch (error) {
// try 代码段中执行有错误时,会执行 catch 代码段
// 查看错误信息
console.log(error.message)
// 终止代码继续执行
return
}
finally {
alert('执行')
}
console.log('如果出现错误,我的语句不会执行')
}
foo()
</script>
总结:
try...catch用于捕获错误信息- 将预估可能发生错误的代码写在
try代码段中 - 如果
tyr代码段中出现错误后,会执行catch代码段,并截获到错误信息
this
默认值
this是JavaScript最具魅惑的知识点,不同应用场合this的取值可能有意想不到的结果,在次我们对以往学过的关于this默认的取值 情况进行归纳和总结。
普通函数
普通函数的调用方式决定了this的值,即【谁调用this的值指向谁】,如下代码所示:
<script>
// 普通函数
function sayHi() {
console.log(this)
}
// 函数表达式
const sayHello = function () {
console.log(this)
}
// 函数的调用方式决定了 this 的值
sayHi() // window
window.sayHi()
// 普通对象
const user = {
name: '小明',
walk: function () {
console.log(this)
}
}
// 动态为 user 添加方法
user.sayHi = sayHi
uesr.sayHello = sayHello
// 函数调用方式,决定了 this 的值
user.sayHi()
user.sayHello()
</script>
注:普通函数没有调用者时this值为window,严格模式下没有调用者时this的值为undefined。
箭头函数
箭头函数中的this与普通函数完全不同,也不受调用方式的影响,事实上箭头函数中并不存在this!箭头函数中访问的this不过是箭头函数所在作用域this变量。
<script>
console.log(this) // 此处为 window
// 箭头函数
const sayHi = function() {
console.log(this) // 该箭头函数中的 this 为函数声明环境中 this 一致
}
// 普通对象
const user = {
name: '小明',
// 该箭头函数中的 this 为函数声明环境中 this 一致
walk: () => {
console.log(this)
},
sleep: function () {
let str = 'hello'
console.log(this)
let fn = () => {
console.log(str)
console.log(this) // 该箭头函数中的 this 与 sleep 中的 this 一致
}
// 调用箭头函数
fn();
}
}
// 动态添加方法
user.sayHi = sayHi
// 函数调用
user.sayHi()
user.sleep()
user.walk()
</script>
在开发中【使用箭头函数前需要考虑函数中的this的值】,事件回调函数使用箭头函数时,this为全局的window,因此DOM事件回调函数不推荐使用箭头函数,如下代码所示:
<script>
// DOM 节点
const btn = document.querySelector('.btn')
// 箭头函数 此时 this 指向了 window
btn.addEventListener('click', () => {
console.log(this)
})
// 普通函数 此时 this 指向了 DOM 对象
btn.addEventListener('click', function () {
console.log(this)
})
</script>
同样由于箭头函数this的原因,基于原型的面向对象也不推荐箭头函数,如下代码所示:
<script>
function Person() {
}
// 原型对像上添加了箭头函数
Person.prototype.walk = () => {
console.log('人都要走路...')
console.log(this); // window
}
const p1 = new Person()
p1.walk()
</script>
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 值为 user
sayHi.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方法调用函数时,第一个参数为this指定的值 call方法的其余参数会依次自动传入函数作为函数的参数
apply
使用apply方法调用函数,同时指定函数中this的值,使用方法如下代码所示:
<script>
// 普通函数
function sayHi() {
console.log(this)
}
let user = {
name: '小明',
age: 18
}
let student = {
name: '小红',
age: 16
}
// 调用函数并指定 this 的值
sayHi.apply(user) // this 值为 user
sayHi.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方法调用函数时,第一个参数为this指定的值 apply方法 第 二个参数为数组,数组的单元值依次自动传入函数作为函数的参数
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的值。
防抖节流
- 防抖(debounce)
所谓防抖,就是触发事件后在n秒内函数只执行一次,如果在n秒内又触发了事件,则会重新计算函数的执行时间
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" id="txt">
<script>
/*
// 防抖
1. 将事件处理函数单独封装
2. 定义一个闭包(防抖函数)
*/
// 1. 将事件处理函数单独封装
function kd() {
console.log(`hello`);
}
// 2. 定义一个闭包(防抖函数)
function debounce(handler, t) {
let timer = null;
return function () {
clearTimeout(timer)
timer = setTimeout(handler, t)
}
}
document.querySelector('#txt').addEventListener('keydown', debounce(dk, 200))
</script>
<script>
/*
// 通过一个计时器,让连续触发的事件在仅执行最后1次
let i = 0;
document.querySelector('#txt').addEventListener('keydown',function(){
console.log(`第 ${++i} 次向服务器发起请求`);
})
*/
</script>
</body>
</html>
- 节流(throttle)
所谓节流,就是指连续触发事件但是在n秒内只执行一次函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box{
width: 200px;
height: 200px;
background:#09f;
color:#f90;
font-size: 30px;
text-align: center;
}
</style>
</head>
<body>
<div class="box"></div>
<script>
// 节流方案实现的效果是什么?
// 事件会被触发很多次,通过判断事件触的间隔时间控制事件处理函数在n秒内仅执行1次。
/*
function clk(){
console.log('事件处理函数被执行');
}
function outer(){
let t = 0;
function inner(){
// 获取当前的时间, 1652692426768
if(Date.now() - t > 2000){
clk();
t = Date.now()
}
}
return inner;
}
let f = outer();
document.querySelector('.box').addEventListener('mousemove', f)
*/
// 1. 真正的事件处理函数
function clk(){
// 正确的事件处理函数
console.log(1);
}
// 2. 这个函数就叫节流函数
function outer(handle,time){
// 3. 利用时间差,来控制真正的事件处理函数的调用
// 3.1 先读取一个时间
let t = Date.now() // 1652692430769
function inner(){
// 3.2 再获取当前时间与3.1读取的时间判断
if(Date.now() - t > time){ ///1652692430769
// 3.3 如果达到时间间隔才调用真正的事件处理函数
handle();
// 3.4 使用当前的时间,去覆盖t
t = Date.now()
}
}
return inner;
}
// 4. 绑定时,节流函数()
document.querySelector('.box').addEventListener('click', outer(clk,2000))
</script>
<script>
/*
// 思路:
1. 将原来的事件处理函数,单独封装成一个独立的函数
2. 定义一个节流函数(闭包)
3. 在节流函数内,通过获取时间减,来调用真正的事件处理函数
4. 注册事件时,注册节流函数
*/
</script>
</body>
</html>