javascript高级进阶技巧,js中的防抖与节流
墨初 知识笔记 132阅读
首先浅拷贝和深拷贝只针对引用类型
浅拷贝拷贝的是地址

常见方法
拷贝对象Object.assgin() / 展开运算符 {…obj} 拷贝对象拷贝数组Array.prototype.concat() 或者 […arr]如果是简单数据类型拷贝值引用数据类型拷贝的是地址 (简单理解 如果是单层对象没问题如果有多层就有问题)

首先浅拷贝和深拷贝只针对引用类型
深拷贝拷贝的是对象不是地址
常见方法
通过递归实现深拷贝lodash/cloneDeep通过JSON.stringify()实现 递归实现深拷贝函数递归
如果一个函数在内部可以调用其本身那么这个函数就是递归函数
简单理解:函数内部自己调用自己, 这个函数就是递归函数递归函数的作用和循环效果类似由于递归很容易发生“栈溢出”错误stack overflow所以必须要加退出条件 return<body> <script> const obj { uname: pink, age: 18, hobby: [乒乓球, 足球], family: { baby: 小pink } } const o {} // 拷贝函数 function deepCopy(newObj, oldObj) { debugger for (let k in oldObj) { // 处理数组的问题 一定先写数组 在写 对象 不能颠倒 if (oldObj[k] instanceof Array) { newObj[k] [] // newObj[k] 接收 [] hobby // oldObj[k] [乒乓球, 足球] deepCopy(newObj[k], oldObj[k]) } else if (oldObj[k] instanceof Object) { newObj[k] {} deepCopy(newObj[k], oldObj[k]) } else { // k 属性名 uname age oldObj[k] 属性值 18 // newObj[k] o.uname 给新对象添加属性 newObj[k] oldObj[k] } } } deepCopy(o, obj) // 函数调用 两个参数 o 新对象 obj 旧对象 console.log(o) o.age 20 o.hobby[0] 篮球 o.family.baby 老pink console.log(obj) console.log([1, 23] instanceof Object) // 复习 // const obj { // uname: pink, // age: 18, // hobby: [乒乓球, 足球] // } // function deepCopy({ }, oldObj) { // // k 属性名 oldObj[k] 属性值 // for (let k in oldObj) { // // 处理数组的问题 k 变量 // newObj[k] oldObj[k] // // o.uname pink // // newObj.k pink // } // } </script></body>
js库lodash里面cloneDeep内部实现了深拷贝 <body> <!-- 先引用 --> <script src./lodash.min.js></script> <script> const obj { uname: pink, age: 18, hobby: [乒乓球, 足球], family: { baby: 小pink } } const o _.cloneDeep(obj) console.log(o) o.family.baby 老pink console.log(obj) </script></body>
JSON序列化 <body> <script> 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) </script></body>
异常处理 了解 JavaScript 中程序异常处理的方法提升代码运行的健壮性。
throw异常处理是指预估代码执行过程中可能发生的错误然后最大程度的避免错误的发生导致整个程序无法继续运行
总结
throw 抛出异常信息程序也会终止执行throw 后面跟的是错误提示信息Error 对象配合 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
代码段中如果 try
代码段中出现错误后会执行 catch
代码段并截获到错误信息 debugger 相当于断点调试
处理this了解函数中 this 在不同场景下的默认值知道动态指定函数 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
方法调用函数同时指定函数中 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
方法调用函数时第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 值为 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
方法调用函数时第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
的值。
所谓防抖就是指触发事件后在 n 秒内函数只能执行一次如果在 n 秒内又触发了事件则会重新计算函数执行时间节流throttle
所谓节流就是指连续触发事件但是在 n 秒中只执行一次函数