20-06-浅copy和深copy、数据劫持、防抖节流

  • 浅copy copy后的结果如果修改了 会影响之前的数据 copy前后 两个数据之前有关系
  • 深copy copy后的结果如果修改了 不会影响之前的数据 copy前后 两个数据之前没有任何关系。说到深copy,通常不是指基本数据类型 是指引用数据类型

浅copy

copy后的结果如果修改了 会影响之前的数据 copy前后 两个数据之前有关系

<script>
    let arr1 = [1,2,3];
    let arr2 = arr1;  // 浅copy
    arr2[0] = 666;
    console.log(arr1)	//[666, 2, 3]
</script>

深copy

  • 浅copy copy后的结果如果修改了 会影响之前的数据 copy前后 两个数据之前有关系
  • 深copy copy后的结果如果修改了 不会影响之前的数据 copy前后 两个数据之前没有任何关系。说到深copy,通常不是指基本数据类型 是指引用数据类型

JSON.stringfy + JSON.parse

实现深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自己拥有的属性,不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秒)起至现在的总毫秒数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值