copy后的结果如果修改了 会影响之前的数据 copy前后 两个数据之前有关系
<script> let arr1 = [1,2,3]; let arr2 = arr1; // 浅copy arr2[0] = 666; console.log(arr1) //[666, 2, 3] </script>实现深copy非常简单的方案
JSON.stringify()将对象转化为json字符串JSON.parse()将json字符串转化为对象 <script> let obj = { name: "wangcai", arr: [1, 2, 3], say: function () { console.log("say...") } } let newObj = JSON.parse(JSON.stringify(obj)); console.log(newObj) //wagncai obj.name = "xiaoqiang"; console.log(newObj) //wangcai console.log(obj) //xiaoqiang </script>有缺点:如果数据中有方法,copy不过来
实现深copy一般只copy自己拥有的属性,不copy原型对象上的属性。 遍历对象使用for in
<script> // 封装一个函数,实现深copy, 只copy自己有的属性,不copy原型上的属性 function deepClone(target){ let cloneTarget = {}; // 遍历数组使用for循环 或 forEach // 遍历对象使用for in for(let key in target){ // 并没有实现深copy, 修改一个,另一个也是会受影响 // cloneTarget[key] = target[key] // 只copy自己的属性,不copy原型上的属性 if(target.hasOwnProperty(key)){ cloneTarget[key] = target[key] } } return cloneTarget; } let obj = { name:"wangcai", arr:[1,2,3], say:function () { console.log("say...") } } //let obj = { // name:"wangcai", // age:1 //} 如果是基本数据类型可以实现深copy let newObj = deepClone(obj) console.log(newObj) obj.arr[1] = 666; console.log(newObj) </script>上面的代码实现的深copy不完整。只能实现基本数据类型,不能实现引用数据类型。
<script> // 如果说一个容器中的数据都是基本数据类型,下面的deepClone可以实现深copy function deepClone(target){ if(typeof target !== "object") return target; //返回的可能是对象,也可能是数组 let cloneTarget = new target.constructor for(let key in target){ if(target.hasOwnProperty(key)){ // target[key] 可能还是引用数据类型 cloneTarget[key] = deepClone(target[key]) } } return cloneTarget; } let obj = { name:"wangcai", age:1, score:[1,2,3] } // let obj = [1,2,3]; let newObj = deepClone(obj) console.log(newObj) obj.score[0] = 666; console.log(newObj) </script>完整深copy
<script> function deepClone(target){ // 对特殊情况的处理 if(target == null) return null; if(target instanceof Date) return new Date(target); if(target instanceof RegExp) return new RegExp(target); // .... // 递归的出口 if(typeof target !== "object") return target; let cloneTarget = new target.constructor for(let key in target){ if(target.hasOwnProperty(key)){ // target[key] 可能还是引用数据类型 cloneTarget[key] = deepClone(target[key]) } } return cloneTarget; } let reg = new RegExp('abc'); let newObj = deepClone(reg) console.log(newObj) </script>数据劫持:Object.defineProperty() 指的是在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果。 比较典型的是Object.defineProperty()和 ES2016 中新增的Proxy对象。数据劫持最著名的应用当属双向绑定
<script> function update() { console.log("更新视图了...") } //劫持 function hijack(obj) { if(typeof obj !== "object" || obj == null){ return obj; } for (let key in obj) { // 把name和age做成响应式数据 获取/设置 我们都要知道 defineReactive(obj,key,obj[key]); } } function defineReactive(obj,key,value){ // 精细化设置某个属性 Object.defineProperty(obj,key,{ get(){ //获取器 // console.log("获取了属性") return value; }, set(newValue){ //设置器 value = newValue update(); } }) } let o = {name:"wc",age:10} hijack(o) console.log(o.name) o.name = "xiaoqiang"; console.log(o.name) /*console.log(o.name); // 获取name属性 o.name = "xx"; // 设置name属性*/ </script>value有可能还是一个对象 递归侦测
function update() { console.log("通知视图刷新...") } function hijack(obj){ if(typeof obj !== "object" || obj === null){ return obj; } for (let key in obj) { defineReactive(obj,key,obj[key]); } } function defineReactive(obj,key,value){ // value有可能还是一个对象 递归侦测 hijack(value); //o.xxx = {b:2} Object.defineProperty(obj,key,{ get(){ // 获取器 return value; }, set(newValue){ // 设置器 // 保证新设置的值和老值不一样,再去更新视图 if(value !== newValue){ hijack(newValue) //对应 o.xxx.b = 3; value = newValue; update(); } }) } let o = {name:"wangcai",age:10, xxx:{a:1}} o.name = "wangcai"; // 修改前后的值一样,没有必须通知数据刷新 o.xxx = {b:2} // 这个地方会通知视图刷新 console.log("通知视图刷新...") o.xxx.b = 3; // console.log("通知视图刷新...")频繁去触发一个事件,有什么问题? 对浏览器来说
window.onscroll window.onresize window.onmousemove频繁地触发一些事件,造成浏览器的性能问题。 解决:防抖 节流 目的:限制事件的频繁触发。
在函数需要频繁触发时,在规定的时间内,只让最后一次生效,前面的不生效。 适合多次事件一次响应的情况。
场景:
实时搜索联想连续输入文字后,发送ajax请求判断scroll是否滚动到底部 <button id="debounce">函数的防抖</button> <script> // 防抖 靠定时器 function handleClick(e) { console.log("点击事件...") } function debounce(callback,delay){ let that = this; let args = arguments; return function () { // 清除待执行的定时器任务 if(callback.timeoutId){ clearTimeout(callback.timeoutId) } callback.timeoutId = setTimeout(function () { callback.apply(that,args) delete callback.timeoutId; },delay) } } // 防抖 时间段内,最后一次执行才算数 document.getElementById("debounce").onclick = debounce(handleClick,2000); // document.getElementById("debounce").onclick = handleClick; </script>在函数需要频繁触发时,函数触发一次后,在规定时间,不会触发第二次,只有大于设定的周期后才会执行第二次。 适合多次事件按时间做平均分配触发。
场景:
调整窗口大小 resize页面滚动 scroll拖拽抢购按钮 mousedown <button id="throttle">函数的节流</button> <script> //函数handleClick(e)可以有事件对象 function handleClick() { // console.log(e) // console.log(Date.now()) console.log("点击事件...") } // 实现节点函数 // 前提是:频繁触发 节流目的:每隔一断时间,执行一次 function throttle(callback,delay){ // 第1次点击 立即调用 let start = 0; return function () { let current = Date.now(); // console.log(current) if(current - start > delay){ //当有事件对象时,callback换为callback.apply(this,arguments) callback() start = current; } } } // document.getElementById("throttle").onclick = handleClick; document.getElementById("throttle").onclick = throttle(handleClick,1000); </script>Date.new() 时间戳,时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总毫秒数
