定制高端网站,郑州市惠济区城乡建设局网站,延边州住房和城乡建设局网站,wordpress 函数教程视频站在巨人的肩膀上 黑马程序员前端JavaScript入门到精通全套视频教程#xff0c;javascript核心进阶ES6语法、API、js高级等基础知识和实战教程 #xff08;十五#xff09;高阶技巧
1. 深浅拷贝
开发中我们经常需要复制一个对象。如果直接用赋值会有下面问题#xff1a;/…站在巨人的肩膀上 黑马程序员前端JavaScript入门到精通全套视频教程javascript核心进阶ES6语法、API、js高级等基础知识和实战教程 十五高阶技巧
1. 深浅拷贝
开发中我们经常需要复制一个对象。如果直接用赋值会有下面问题// 一个 pink 对象
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 }
// 但是 pink 对象里面的 name 值也发生了变化
console.log(pink) // { name: red老师, age: 18 }1.1 浅拷贝
首先浅拷贝和深拷贝只针对引用类型。浅拷贝拷贝的是地址常见方法 拷贝对象Object.assgin() / 展开运算符 {…obj} 拷贝对象拷贝数组Array.prototype.concat() 或者 […arr] 例子1const obj {uname: pink
}
const o { ...obj }
console.log(o) // { uname: pink }
o.uname red
console.log(o) // { uname: red }
console.log(obj) // { uname: pink }例子2// 一个 pink 对象
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 }
// 不会影响 pink 对象
console.log(pink) // { name: pink老师, age: 18 }例子3 js // 一个 pink 对象 const pink { name: ‘pink老师’, age: 18, family: { mother: ‘pink妈妈’ } } const red {} Object.assign(red, pink) console.log(red) // { name: ‘pink老师’, age: 18 } red.name ‘red老师’ // 更改对象里面的 family 还是会有影响 console.log(red) // { name: ‘red老师’, age: 18 } // 不会影响 pink 对象 console.log(pink) // { name: ‘pink老师’, age: 18 }- 如果是简单数据类型拷贝值引用数据类型拷贝的是地址 (简单理解如果是单层对象没问题如果有多层就有问题)直接赋值和浅拷贝有什么区别 直接赋值的方法只要是对象都会相互影响因为是直接拷贝对象栈里面的地址浅拷贝如果是一层对象不相互影响如果出现多层对象拷贝还会相互影响 浅拷贝怎么理解 拷贝对象之后里面的属性值是简单数据类型直接拷贝值如果属性值是引用数据类型则拷贝的是地址
1.2 深拷贝
首先浅拷贝和深拷贝只针对引用类型深拷贝拷贝的是对象不是地址常见方法 通过递归实现深拷贝lodash/cloneDeep通过JSON.stringify()实现
1.2.1 通过递归实现深拷贝
函数递归 如果一个函数在内部可以调用其本身那么这个函数就是递归函数 简单理解函数内部自己调用自己, 这个函数就是递归函数递归函数的作用和循环效果类似由于递归很容易发生“栈溢出”错误stack overflow所以必须要加退出条件return例子let num 1
// fn 就是递归函数
function fn() {console.log(我要打印6次)if(num 6) {return}numfn() // 函数内部调用函数自己
}
fn()利用递归函数实现 setTimeout 模拟 setInterval效果。function getTime() {const time new Date().toLocalString()console.log(time)setTimeout(getTime, 1000) // 定时器调用当前函数
}
getTime()通过递归函数实现深拷贝简版const o {}
function deepCopy(newObj, oldObj) {for(let k in oldObj) {if(oldObj[k] instanceof Array) {newObj[k] []deepCopy(newObj[k], oldObj[k])}else if(oldObj[k] instanceof Object) {newObj[k] {}deepCopy(newObj[k], oldObj[k])}else {newObj[k] oldObj[k]}}
}1.2.2 js库lodash里面cloneDeep内部实现了深拷贝
const obj {uname: pink,age: 18,hobby: [篮球, 足球],family: {baby: 小pink}
}
// 语法_.cloneDeep(要被克隆的对象)
const o _.cloneDeep(obj)
console.log(o)
o.family.baby 老pink
console.log(obj)1.2.3 通过JSON.stringify()实现
const obj {uname: pink,age: 18,hobby: [篮球, 足球],family: {baby: 小pink}
}
const o JSON.parse(JSON.stringify(obj))
console.log(o)
o.family.baby 老pink
console.log(obj)2. 异常处理
2.1 throw 抛异常
异常处理是指预估代码执行过程中可能发生的错误然后最大程度的避免错误的发生导致整个程序无法继续运行总结 throw 抛出异常信息程序也会终止执行throw 后面跟的是错误提示信息Error 对象配合 throw 使用能够设置更详细的错误信息 例子function counter(x, y) {if(!x || !y) {// throw 参数不能为空!;throw new Error(参数不能为空!)}return x y
}
counter()2.2 try/catch 捕获异常
我们可以通过try / catch 捕获错误信息浏览器提供的错误信息 try 试试 catch 拦住 finally 最后总结 try…catch 用于捕获错误信息将预估可能发生错误的代码写在 try 代码段中如果 try 代码段中出现错误后会执行 catch 代码段并截获到错误信息finally 不管是否有错误都会执行 例子function foo() {try {// 查找 DOM 节点const p document.querySelectro(.p)p.style.color red} catch(error) {// try 代码段中执行有错误时会执行 catch 代码段// 查看错误信息console.log(error.message)// 终止代码继续执行return}finally {alert(执行)}console.log(如果出现错误我的语句不会执行)
}
foo()2.3 debugger
我们可以通过 try / catch 捕获错误信息浏览器提供的错误信息例子const arr [1, 3, 5]
const newArr arr.map((item, index) {debuggerconsole.log(item) // 当前元素console.log(index) // 当前元素索引号return item 10 // 让当前元素 10
})
console.log(newArr) // [11, 13, 15]3. 处理this
this 是 JavaScript 最具“魅惑”的知识点不同的应用场合 this 的取值可能会有意想不到的结果在此我们对以往学习过的关于【 this 默认的取值】情况进行归纳和总结。
3.1 this指向
3.1.1 普通函数this指向
普通函数的调用方式决定了 this 的值即【谁调用 this 的值指向谁】普通函数没有明确调用者时 this 值为 window严格模式下没有调用者时 this 的值为 undefined。例子1// 普通函数
function sayHi() {console.log(this)
}
// 函数表达式
const sayHello function() {console.log(this)
}
// 函数的调用方式决定了 this 的值
sayHi() // window
windows.sayHi()例子2// 普通对象
const user {name: 小明,walk: function() {console.log(this)}
}
// 动态为 user 添加方法
user.sayHi sayHi
user.sayHello sayHello
// 函数的调用方式决定了 this 的值
user.sayHi()
user.sayHello()例子3scriptuse strictfunction fn() {console.log(this) // undefined}fn()
/script3.1.2 箭头函数this指向
箭头函数中的 this 与普通函数完全不同也不受调用方式的影响事实上箭头函数中并不存在 this 箭头函数会默认帮我们绑定外层 this 的值所以在箭头函数中 this 的值和外层的 this 是一样的箭头函数中的this引用的就是最近作用域中的this向外层作用域中一层一层查找this直到有this的定义 例子1console.log(this) // 此处为 window
// 箭头函数
const sayHi function() {console.log(this) // 该箭头函数中的 this 为函数声明环境中 this 一致
}例子2// 普通对象
const user {name: 小明,// 该箭头函数中的 this 为函数声明环境中 this 一致walk: () {console.log(this)}
}注意情况1 在开发中【使用箭头函数前需要考虑函数中 this 的值】事件回调函数使用箭头函数时this 为全局的 window因此DOM事件回调函数如果里面需要DOM对象的this则不推荐使用箭头函数例子// DOM 节点
const btn document.querySelector(.btn)
// 箭头函数此时 this 指向了 window
btn.addEventListener(click, () {console.log(this)
})
// 普通函数此时 this 指向了 DOM 对象
btn.addEventListener(click, function () {console.log(this)
})注意情况2 同样由于箭头函数 this 的原因基于原型的面向对象也不推荐采用箭头函数例子function Person() {
}
// 原型对象上添加了箭头函数
Person.prototype.walk () {console.log(人都要走路...)console.log(this); // window
}
const p1 new Person()
p1.walk()总结 函数内不存在this沿用上一级的不适用 构造函数原型函数dom事件函数等等 适用 需要使用上层this的地方 使用正确的话它会在很多地方带来方便后面我们会大量使用慢慢体会
3.2 改变this
JavaScript 中还允许指定函数中 this 的指向有 3 个方法可以动态指定普通函数中 this 的指向 call()apply()bind()
3.2.1 call()–了解
使用 call 方法调用函数同时指定被调用函数中 this 的值语法fun.call(thisArg, arg1, arg2, ...)thisArg在 fun 函数运行时指定的 this 值arg1arg2传递的其他参数返回值就是函数的返回值因为它就是调用函数 例子1const obj {name: pink
}
function fn() {console.log(this) // 指向 obj {name: pink}
}
fn.call(obj)例子2const obj {name: pink
}
function fn(x, y) {console.log(this) // 指向 obj {name: pink}console.log(x y) // 传递过来的参数相加
}
fn.call(obj, 1, 2)3.2.2 apply()-理解
使用 apply 方法调用函数同时指定被调用函数中 this 的值语法fun.apply(thisArg, [argsArray])thisArg在fun函数运行时指定的 this 值argsArray传递的值必须包含在数组里面返回值就是函数的返回值因为它就是调用函数因此 apply 主要跟数组有关系比如使用 Math.max() 求数组的最大值 例子// 求和函数
function counter(x, y) {return x y
}
// 调用 counter 函数并传入参数
let result counter.apply(null, [5, 10])
console.log(result)求数组最大值2个方法// 求数组最大值
const arr [3, 5, 2, 9]
console.log(Math.max.apply(null, arr)) // 9利用apply
console.log(Math.max(...arr)) // 9利用展开运算符3.2.3 bind()-重点
bind() 方法不会调用函数。但是能改变函数内部this 指向语法fun.bind(thisArg, arg1, arg2, ...)thisArg在 fun 函数运行时指定的 this 值arg1arg2传递的其他参数返回由指定的 this 值和初始化参数改造的 原函数拷贝 新函数因此当我们只是想改变 this 指向并且不想调用这个函数的时候可以使用 bind比如改变定时器内部的this指向. 例子// 普通函数
function sayHi() {console.log(this)
}
let user {name: 小明,age: 18
}
// 调用 bind 指定 this 的值
let sayHello sayHi.bind(user);
// 调用使用 bind 创建的新函数
sayHello()3.2.4 call apply bind 总结
相同点: 都可以改变函数内部的this指向. 区别点: call 和 apply 会调用函数, 并且改变函数内部this指向.call 和 apply 传递的参数不一样, call 传递参数 aru1, aru2…形式apply 必须数组形式[arg]bind 不会调用函数, 可以改变函数内部this指向. 主要应用场景: call 调用函数并且可以传递参数apply 经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值bind 不调用函数但是还想改变this指向. 比如改变定时器内部的this指向.
4. 性能优化
4.1 防抖
所谓防抖debounce就是指触发事件后在 n 秒内函数只能执行一次如果在 n 秒内又触发了事件则会重新计算函数执行时间现实例子北京买房政策需要连续5年的社保如果中间有一年断了社保则需要从新开始计算开发使用场景搜索框防抖 假设输入就可以发送请求但是不能每次输入都去发送请求输入比较快发送请求会比较多我们设定一个时间假如300ms 当输入第一个字符时候300ms后发送请求但是在200ms的时候又输入了一个字符则需要再等300ms 后发送请求 利用防抖来处理-鼠标滑过盒子显示文字const box document.querySelector(.box)
let i 1
function mouseMove() {box.innerHTML i
}
function debounce(fn, t 500) {let timeIdreturn function () {// 如果有定时器先清除if(timeId)clearTimeout(timeId)// 开启定时器timeId setTimeout(function() {fn()}, t)}
}
box.addEventListener(mousemove, debounce(mouseMove, 500))核心思路利用定时器实现当鼠标滑过判断有没有定时器还有就清除以最后一次滑动为准开启定时器
4.2 节流 所谓节流throttle就是指连续触发事件但是在 n 秒中只执行一次函数 现实例子只有等到了上一个人做完核酸整个动作完成了第二个人才能排队跟上 开发使用场景小米轮播图点击效果、鼠标移动、页面尺寸缩放resize、滚动条滚动 就可以加节流 假如一张轮播图完成切换需要300ms不加节流效果快速点击则嗖嗖嗖的切换加上节流效果不管快速点击多少次300ms时间内只能切换一张图片。 利用节流来处理-鼠标滑过盒子显示文字 const box document.querySelector(.box)
let i 1
function mouseMove() {box.innerHTML i// 如果存在开销较大操作大量数据处理大量dom操作可能会卡
}
function throttle(fn, t 500) {let startTime 0return function () {let now Date.now()if(now - startTime t) {fn()startTime now}}
}
box.addEventListener(mousemove, throttle(mouseMove, 500))利用节流的方式鼠标经过500ms数字才显示 节流和防抖的区别是 节流: 就是指连续触发事件但是在 n 秒中只执行一次函数比如可以利用节流实现 1s 之内只能触发一次鼠标移动事件防抖如果在 n 秒内又触发了事件则会重新计算函数执行时间 节流和防抖的使用场景是 节流: 鼠标移动页面尺寸发生变化滚动条滚动等开销比较大的情况下防抖: 搜索框输入设定每次输入完毕n秒后发送请求如果期间还有输入则从新计算时间
4.3 Lodash 库实现节流和防抖 节流 const box document.querySelector(.box)
let i 1
function mouseMove() {box.innerHTML i// 如果存在开销较大操作大量数据处理大量dom操作可能会卡
}
box.addEventListener(mousemove, _.throttle(mouseMove, 1000))防抖 const box document.querySelector(.box)
let i 1
function mouseMove() {box.innerHTML i// 如果存在开销较大操作大量数据处理大量dom操作可能会卡
}
box.addEventListener(mousemove, _.debounce(mouseMove, 1000))5. 节流综合案例
页面打开可以记录上一次的视频播放位置
思路 在ontimeupdate事件触发的时候每隔1秒钟就记录当前时间到本地存储下次打开页面 onloadeddata 事件触发就可以从本地存储取出时间让视频从取出的时间播放如果没有就默认为0s获得当前时间 video.currentTime 代码const video document.querySelector(video)
video.ontimeupdate _.throttle(() {localStorage.setItem(currentTime, video.currentTime)
}, 1000)
video.onloadeeddata () {video.currentTime local.getItem(currentTime) || 0
}