JS笔试手撕题

数据劫持

Vue2的Object.defineProperty()

Vue2的响应式是通过Object.defineProperty()拦截数据,将数据转换成getter/setter的形式,在访问数据的时候调用getter函数,在修改数据的时候调用setter函数。然后利用发布-订阅模式,在数据变动时触发依赖,也即发布更新给订阅者,订阅者收到消息后进行相应的处理

描述符分为数据描述符和存取描述符,只能是其中之一,不可两者同时存在
在这里插入图片描述
configurable:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性,默认值为false。
enumerable:表示能否通过for in循环访问属性,默认值为false
//数据描述符
1.writable:表示能否修改属性的值。默认值为false。
2.value:包含这个属性的数据值。默认值为undefined。
//存取描述符
1.get:在读取属性时调用的函数,默认值是undefined
2.set:在写入属性的时候调用的函数,默认值是undefined

  <script>
        function defineReactive(data) {
            if (!data || Object.prototype.toString.call(data) !== '[object Object]')
                return
            for (let key in data) {
                let val = data[key]
                Object.defineProperty(data, key, {
                    configurable: true,//可配置
                    enumerable: true,//可枚举
                    set: function (newval) {
                        console.log('调用set');
                        if (newval === val) {
                           return ;
                        }
                        console.log('属性值发生变化');
                        val = newval

                    },
                    get() {
                        console.log('调用get');
                        return val
                    },

                })
                if (typeof val === 'object') {
                    defineReactive(val)
                }
            }
        }
        const data = {
            name: 'better',
            firends: ['1', '2']
        }
        defineReactive(data)

        console.log('data.friends[0]:', data.firends[0]);
        console.log('data.name:', data.name);
        data.name = 'lihua'
        console.log('更改后的data.name:', data.name);
        data.friends[0] = '100'
        // 在JavaScript中,数组是一种特殊的对象,因此直接对数组进行defineProperty并不能监听数组元素的变化。
        // 当我们尝试修改数组元素时,比如data.friends[0] = 100,实际上并不会触发set函数,也就无法实现数组元素的响应式更新。
        console.log('更改后的data.friends[0]:', data.firends[0]);

    </script>

在这里插入图片描述
弊端:
1.无法劫持对象的添加和删除操作,Vue2的解决方法(新增set和delete)
2.无法劫持到数组的api(push、pop…),Vue2的解决方法是重写数组的api
3.存在深层嵌套关系,通过无脑递归,但也会劫持到项目始终都不会用到的对象,造成性能损耗

Vue3的proxy(解决vue2响应式的弊端)

Proxy(代理):用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
Reflect(反射):对源对象的属性进行操作

Vue3基于ES6新增的Proxy对象实现数据代理以及通过Reflect对源数据进行操作,它解决了Vue2中无法追踪数据新增或删除属性的问题。另外,Proxy可以直接监听数组,无需像Vue2响应式那样需要重写数组方法进行拦截

proxy详解

const p = new Proxy(target, handler)
//target:要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
//handler:一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。
 <script>

        function isObject(obj) {
            if (typeof obj !== "object" || !obj) {
                return false;
            }
            return true;
        }

        // vue3使用reactive定义响应式对象时返回的就是reactive的实例
        function reactive(obj) {
            // 对整个对象进行劫持
            return new Proxy(obj, {
                get(target, key) {
                    console.log('get:', key);
                    // proxy想要监听深层对象也需要进行递归处理,不过这里用懒加载,用不到的属性不用进行深层监听
                    let res = Reflect.get(target, key)
                    return isObject(res) ? reactive(res) : res;
                },
                set(target, key, value) {
                    console.log('set:', key, value);
                    return Reflect.set(target, key, value)
                },
                deleteProperty(target, key) {
                    console.log('delete', key);
                    return Reflect.deleteProperty(target, key)
                }
            })
        }

        let person = reactive({ name: 'zs', age: 18, job: { code: "前端" } })
        console.log('person.name:', person.name);
        // 直接对proxy实例操作
        person.name = '小张'
        console.log('------------');
        person.gender = '男'
        delete person.age
        console.log('------------');
        console.log('测试对数组的监听');
        //数组的push方法,作为push的属性理应也该被劫持到
        let arr = reactive([1, 2, 3, 4, 5])
        arr.push(11)

        console.log('--------------------');
        console.log('对深层对象的测试');
        console.log('person.job.code:', person.job.code);
        person.job.code = "php"


    </script>

在这里插入图片描述

总结
优点

1.Proxy直接劫持整个对象,并返回一个新对象,我们可以只操作新的对象达到响应式的目的
2.Proxy可以直接监听数组的变化(push,shift,splice…)
3.Proxy有多达13中拦截方法,不限于apply、ownKeys、deleteProperty、has等等,这是Object.defineProperty所不具备的
4.Proxy懒加载,解决递归造成的性能问题

缺点

Proxy不兼容IE

闭包

 <script>
        function fn1() {
            debugger;
            let num = 999;
            function innerFn1() {
                debugger;
                console.log(num);
            }
            innerFn1()
        }
        fn1()
    </script>

在这里插入图片描述
函数依赖外部变量就形成闭包
在这里插入图片描述

闭包的应用场景

函数内部return出去一个函数,就是结合了闭包的应用场景之一,目的是为了让那个闭包持久化

    <script>
        function fn1() {
            debugger;
            let num = 999;
            function innerFn1() {
                debugger;
                console.log(num);
            }
           return innerFn1
        }
        let closure1=fn1();//闭包 num:999
        // 两个不同的闭包,因此闭包可以解决全局变量命名冲突问题
        let closure2=fn1();//闭包 num:999
    </script>
<script>
        // 面试题
        function fn3(a, b) {
            console.log('第二个参数的值是:', b);
            return {
                innerFn: function (c) {
                    return fn3(c, a);
                }
            }
        }
        // 链式调用
        fn3(1).innerFn(2).innerFn(3).innerFn(4)
        // 1.外部调用 传递a:1 返回一个对象 形成闭包 (a:1)=>输出undefined
        // 2.传递c:2 执行函数,并返回一个对象  fn3(c,a) (c:2,a:1)=>输出1
        // 3.外部a:2,b:1,返回一个对象(a:2)
        // 4.参数c:3,执行fn(3,2)   外部:(a:3,b:2)=>输出2
        // 5.闭包(a:3)
    </script>

在这里插入图片描述

        // 面试题
        function fn(a, b) {
            debugger
            console.log('第二个参数的值是:', b);
            return {
                //对象中的函数形成一个闭包
                fn: function (c) {
                    debugger
                    return fn(c, a)
                }
            }
        }
        let obj = fn(1)
        // 一个新对象有一个闭包=>1个
        // 访问的都是同一个闭包
        obj.fn(2)
        obj.fn(3)
        obj.fn(4)

在这里插入图片描述

 <script>
        // 面试题
        function fn(a, b) {
            debugger
            console.log('第二个参数的值是:', b);
            return {
                //对象中的函数形成一个闭包
                fn: function (c) {
                    debugger
                    return fn(c, a)
                }
            }
        }
        // let obj = fn(1)
        // // 一个新对象有一个闭包=>1个
        // // 访问的都是同一个闭包
        // obj.fn(2)
        // obj.fn(3)
        // obj.fn(4)

        console.log('----------------');
        let a = fn(1).fn(2)
        a.fn(3).fn(4)
        a.fn(5).fn(6)

    </script>

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/603448.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Windows下启动Tomcat显示乱码解决办法

1、Windows下启动Tomcat显示乱码 2、解决办法 找到 D:\apache-tomcat-9.0.89\conf下的logging.properties&#xff0c;找到java.util.logging.ConsoleHandler.encoding的值改为GBK&#xff0c;就可以了 完美解决&#xff01;显示正常的中文了

网络安全之二层局域网封装及广域网封装详解

局域网封装&#xff1a;Ethernet2&#xff08;TCP/IP&#xff09;&#xff0c;IEEE802.3&#xff08;OSI&#xff09;&#xff08;前面文章中讲解了TCP、IP和OSI本文就不继续讲解&#xff1a;可以查看&#xff1a;网络安全之OSI七层模型详解-CSDN博客&#xff09; 广域网封装&…

『ZJUBCA Collaboration』WTF Academy 赞助支持

非常荣幸宣布&#xff0c;浙江大学区块链协会收到WTF Academy的赞助与支持&#xff0c;未来将共同开展更多深度合作。 WTF Academy是开发者的Web3开源大学&#xff0c;旨在通过开源教育让100,000名开发者进入到Web3。截止目前&#xff0c;WTF开源教程在GitHub收获超15,000 ⭐&a…

环形链表问题详解

引言 环形链表的题大家都应该做过&#xff0c;如果没有做过可以去某扣上做一下 ,下面有传送门 141. 环形链表 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/linked-list-cycle/submissions/530160081/ 正文 如果在面试的情况下出现了环形链表的题大…

QLineEdit 最右侧添加按钮

如果采用QLineEdit + QPushButton的方式的话,无法将按钮放到QLineEdit的输入框内部,所以下面的方法可以将按钮放到QLineEdit内部的最右侧,效果: 代码如下: QLineEdit* editor = new QLineEdit(parent); QToolButton* btn = new QToolButton; btn->setText("...&q…

【噪声学习】噪声标签的鲁棒点云分割

Robust Point Cloud Segmentation with Noisy Annotations 事实上,与二维图像标注[1]、[2]相比,三维数据的干净标签更难获得。这主要是因为1)需要标注的点数通常非常庞大,例如在 ScanNetV2 [3] 中标注一个典型的室内场景时,需要标注百万量级的点数;2)标注过程本身更加复…

使用SmartEDA电路仿真软件,让这五件事变得很简单

SmartEDA电路仿真软件&#xff1a;轻松实现电子设计五大突破 在电子设计领域&#xff0c;SmartEDA电路仿真软件以其强大的功能和用户友好的界面&#xff0c;成为了设计师们的得力助手。这款软件不仅简化了电路设计流程&#xff0c;还提高了设计效率&#xff0c;让以下五件事情…

【驱动】I2C读写时序

1、I2C总线 I2C使用两条线在主控制器和从机之间通信,SCL(串行时钟线)和SDA(串行数据线),这两条线需接5~10欧上拉电阻,总线空闲空闲时,SCL和SDA处于高电平,I2C总线标准模式速度可以达到100K/S,快速模式可以达到400K/S。 2、状态 I2C总线有四种状态:空闲、启动、忙碌、…

品质为王:高效溶解性鱼油胶囊的软胶囊弹性硬度测试解析

品质为王&#xff1a;高效溶解性鱼油胶囊的软胶囊弹性硬度测试解析 在当今的健康产品市场中&#xff0c;高效溶解性鱼油胶囊以其独特的营养价值和吸收效率赢得了众多消费者的青睐。然而&#xff0c;要想在激烈的市场竞争中脱颖而出&#xff0c;产品的品质保证至关重要。其中&a…

RabbitMQ-基础

RabbitMQ 同步调用 双方交互都是实时的&#xff0c;可以立即返回结果 问题 拓展性差&#xff1a;每次有新的需求&#xff0c;代码经常变动&#xff0c;不符合开闭原则性能下降&#xff1a;调用者需要等待服务提供者分别执行后才返回结果&#xff0c;服务提供者很多情况下会…

看完这个,你就懂了!IT审计到底是干什么的?如何做好IT审计?

01 大家应该都知道财务审计&#xff0c; 通俗讲&#xff0c;就是查账的。 看一下公司账上的数据是否准确&#xff0c; 每笔账是否都能合理溯源。 那IT审计到底是干什么的呢&#xff1f; 它和财务审计有什么关系吗&#xff1f; 这么跟你说吧&#xff0c; 现在很多公司都…

Etcd集群选举细节

日志级别 在 etcd 集群中&#xff0c;领导者选举是 Raft 协议的一部分&#xff0c;用于在当前领导者失败或无法与集群中的其他节点通信时选出新的领导者。以下是您提供的日志中与领导者选举相关的一些关键条目&#xff0c;以及对它们的详细说明&#xff1a; 节点失去领导者&am…

Delta lake with Java--使用stream同步数据

今天继续学习Delta lake Up and Running 的第8章&#xff0c;处理流数据&#xff0c;要实现的效果就是在一个delta表&#xff08;名为&#xff1a;YellowTaxiStreamSource&#xff09;插入一条数据&#xff0c;然后通过流的方式能同步到另外一个delta表 &#xff08;名为&#…

机器人系统可以支持对接人工系统吗?

​ 随着科技的飞速发展&#xff0c;机器人系统在各行各业都扮演着越来越重要的角色。它们可以高效地处理大量数据&#xff0c;执行繁琐的任务&#xff0c;甚至在某些领域超越了人类的能力。然而&#xff0c;机器人系统也有其局限性&#xff0c;特别是在处理复杂的人际交往…

自动驾驶主流芯片及平台架构(四)华为、​高通、英伟达高算力平台

上一章节有提到低算力的自动驾驶平台&#xff0c;本章内容重点介绍高算力的自动驾驶平台&#xff0c;华为、高通、英伟达。 华为自动驾驶MDC平台方案介绍 以整车数字架构为基础&#xff0c;全面管理软硬件的复杂性&#xff0c;并确保整车的可靠性&#xff1a;我们提出华为CCAVe…

批量图片重命名及汇总

又一堆图片文件需要处理... 源文件分布&#xff1a; 有N个文件夹&#xff0c;每个文件夹下又有M个子文件夹&#xff0c;每个子文件夹下有X张图片。 例如文件夹A下有子文件夹A1,A2,A3&#xff0c;子文件夹A1下有图片a-1,a-2,a-3...... 处理目标&#xff1a; 1、将所有图片汇…

算法提高之树的中心

算法提高之树的中心 核心思想&#xff1a;树形dp 换根dp 每个点作为根节点 找其子树的最大距离和父节点的最大距离 dfs1&#xff1a;求子树对于当前根节点的最大距离和次大距离 求次大距离原因&#xff1a;如果当前节点是其父节点子树的最大路径上的点&#xff0c;最大距离不…

基于Linux中的 进程相关知识 综合讲解

目录 一、进程的基本概念 二、pid&#xff0c;ppid&#xff0c;fork函数 三、进程的状态讲解 四、进程的优先级 五、完结撒❀ 一、进程的基本概念 概念&#xff1a; ● 课本概念&#xff1a;程序的一个执行实例&#xff0c;正在执行的程序等 ● 内核观点&#xff1a;担当…

深度学习实例2_车牌识别分割——自学笔记

import cv2 from matplotlib import pyplot as plt import os import numpy as np from PIL import ImageFont, ImageDraw, Image彩色图片显示 def plt_show0(img):b,g,r = cv2.split(img)img = cv2.merge([r, g, b])plt.imshow(img)plt.show()灰度图片显示 def plt_show(img…

暗区突围PC测试资格获取 Twitch老鼠台一键领取测试资格教程

Twitch平台&#xff0c;这个广受欢迎的直播巨头&#xff0c;不仅是游戏文化的直播聚集地&#xff0c;还常与各类游戏携手合作&#xff0c;为观众带来独特的互动体验&#xff0c;观看直播即可解锁游戏内奖励。正值热门游戏《暗区突围》PC版测试阶段&#xff0c;Twitch再次发力&a…