当前位置: 首页 > news >正文

使用递归实现深拷贝

文章目录

    • 为什么要使用递归什么深拷贝
    • 具体实现
      • 基础实现
      • 处理 函数
      • 处理 Symbol
      • 处理 Set
      • 处理 Map
      • 处理 循环引用
    • 结语-源码

为什么要使用递归什么深拷贝

我们知道在 JavaScript 中可以通过使用JSON序列化来完成深拷贝,但是这种方法存在一些缺陷,比如对于函数、Symbol、Map 等等数据是无法处理的,甚至如果是循环引用的话还会造成报错,具体关键JSON的介绍我就不在这里赘述了,有兴趣的可以看看我的另一篇文章JSON详解

在这里插入图片描述

在这里插入图片描述

具体实现

基础实现

  1. 我们先实现一下最简单的,只对普通数据进行处理,数据如下:

    const obj = {name: '张三',age: 18,friends: [{ name: '李四', age: 20 },{ name: '王五', age: 22 }],other:{address:'长沙'}
    }
    
  2. 我们肯定要有一个这样的方法或者函数来帮助我们完成这个深拷贝,如果要两个对象要不关联的话,那就只能是创建一个全新的对象,全新的对象怎么来,对吧,所以我们就可以写出如下代码:

    function deepClone(value) {// 创建对象-用来承载数据const newObj = {}
    }
    
  3. 现在的问题就回到了。我们如何把 obj 的数据赋值给 newObj,肯定不能是直接 newObj = obj,我们要做的就是拿到 obj 对象中的 k、v,通过这个 k、v 重新给 newObj 这个对象创建数据,但是我又不知道这个传入进来的对象具备什么属性和值,所以怎么得到这个 k、v 呢?而这种取出一个数据中的每一项,是不是感觉和数组的遍历很像呢,所以如果可以遍历这个对象的话,是不是就可以拿到呢

  4. 不知道大家有没有记得以前 js 基础接触过的 for…in 方法,这个方法就可以遍历对象,可以遍历对象是不是就表示可以把 obj 所有有的属性和值也给 newObj 对象赋值一下呢?所以我们下一步就应该是使用 for…in 来完成,如下:

    function deepClone(value) {const newObj = {}// 遍历 value 赋值给 newObjfor (let key in value) {newObj[key] = value[key]}return newObj
    }
    
  5. 现在是不是实现了我们的效果呢,不知道,打印结果看一下吧,添加测试代码,如下:

    const newObj = deepClone(obj)console.log('newObj: ', newObj)
    
  6. 结果如图:

    在这里插入图片描述

  7. 值确实是一样的,但是是不是可以实现那种修改 newObj 而不影响 obj 呢?测试一下吧,测试代码如下:

    console.log(newObj === obj)console.log(newObj.friends === obj.friends)console.log(newObj.other === obj.other)newObj.other.address = '上海'console.log(newObj.other.address)console.log(obj.other.address)
    
  8. 输出结果如图:

    在这里插入图片描述

  9. 这就非常的有意思了,newObj 确实不等于 obj 了,表示最外层的引用确实断开了,但是 friends 和 other 属性确还是相等的,且改变了 newObj.other.address 的值,obj 这个地方的值也改变了

  10. 其实细心一点就不难发现,firends 和 other 属性也都是一个对象啊,所以我们是不是也要对他们进行处理呢?那怎么处理呢,针对这种情况和我们处理这个外层的其实是不是一致的啊,而重复这个过程,哪想到了什么,对,本文的主题,递归,所以如果检测到一个值是 对象 的话,就再次递归这个函数执行,修改代码如下:

    function deepClone(value) {const newObj = {}for (let key in value) {// 判断属性值是否为对象,如果是对象则递归调用deepClone函数newObj[key] = typeof value[key] === 'object' ? deepClone(value[key]) : value[key]}return newObj
    }
    
  11. 查看克隆的结果,如图:
    在这里插入图片描述

  12. 从结果上看 friends 这个属性值从数组变成了对象,这是因为数组也是一个对象,而我们处理数组的方式也是重新创建一个 newObj 来存储数组的数据,所以返回的值自然就变成了对象,这时候应该怎么解决呢?

  13. 问题尽然是最开始的 newObj 被赋值为了一个 {},那么我们只要判断当前克隆的值是一个数组的时候,就赋值为 [],否则赋值为 {},是不是就可以解决了,如下:

    function deepClone(value) {// 如果是数组,则创建一个新的数组,否则创建一个新的对象const newObj = Array.isArray(value) ? [] : {}for (let key in value) {newObj[key] = typeof value[key] === 'object' ? deepClone(value[key]) : value[key]}return newObj
    }
    
  14. 结果如图:

    在这里插入图片描述

  15. 现在我们不改动测试语句,查看测试结果,如图:

    在这里插入图片描述

  16. 是不是感觉比较简单呢,剩下的我们需要做一下优化,判断是否是一个对象可以抽离成一个方法,且可以用于开始的边界判断,如下:

    function isObject(value) {return typeof value === 'object' && value !== null
    }
    
  17. 然后使用这个方法优化 deepClone 方法,如下:

    function deepClone(value) {if (!isObject(value)) {return value}const newObj = Array.isArray(value) ? [] : {}for (let key in value) {newObj[key] = isObject(value) ? deepClone(value[key]) : value[key]}return newObj
    }
    

处理 函数

  1. 为了更加直观的看到处理函数类型,我们先暂时对克隆的对象做一些修改,如下:

    const obj = {name: '张三',age: 18,sayHi: function () {console.log('你好啊!')}
    }
    
  2. 首先对于这个函数的处理,我们需要先认清楚一些概念,如果一个函数内部的逻辑非常复杂,我们要通过创建一个新的 Funciton 来实现一个完全的新的函数,也是可以的,但是会非常的复杂,而且没有什么必要,一个函数本身就具备复用性,内部的作用域本身就互不干扰,所以我们只要获取到这个函数,并重新赋值给新拷贝的对象里面的对应的位置即可,这也是一种目前比较常见的做法

  3. 因此我们只要发现当前克隆的值是一个函数的话,直接返回即可,如下:

    function deepClone(value) {// 判断是否是一个函数if (typeof value === 'function') {return value}if (!isObject(value)) {return value}const newObj = Array.isArray(value) ? [] : {}for (let key in value) {newObj[key] = isObject(value) ? deepClone(value[key]) : value[key]}return newObj
    }
    
  4. 现在输出打印克隆的打印结果,如图:

    在这里插入图片描述

处理 Symbol

  1. 我们还是一样先修改一下数据,如下:

    const s1 = Symbol('aaa')
    const s2 = Symbol('bbb')const obj = {name: '张三',age: 18,[s1]: 'aaa',s2: s2
    }
    
  2. 我们使用目前的方法,来看一下克隆的结果,如图:

    在这里插入图片描述

  3. 使用 s1 作为键的属性,没有被拷贝进来,但是使用字符串 s2 的属性和携带的 Symbol 类型的值被拷贝了进来,而且这两个对象里面的 s2 这个 Symbol值是不是同一个呢?测试代码如下:

    console.log(newObj.s2 === obj.s2) // true
    
  4. 就不粘贴结果了,测试中得出的还是同一个 Symbol,所以如果不希望使用同一个 Symbol 的话,我们可以在最开始的时候进行一个判定,如果当前克隆的值是一个 Symbol 的话,就重新创建 Symbol,并使用原来的 Symbol 的 description,如下:

    在这里插入图片描述

    function deepClone(value) {// 判断是否是一个 Symbolif (typeof value === 'symbol') {return Symbol(value.description)}if (typeof value === 'function') {return value}if (!isObject(value)) {return value}const newObj = Array.isArray(value) ? [] : {}for (let key in value) {newObj[key] = isObject(value) ? deepClone(value[key]) : value[key]}return newObj
    }
    
  5. 查看输出结果,如下:

    console.log(newObj.s2 === obj.s2) // false
    
  6. 那为什么 Symbol 作为 key 的时候无法被拷贝呢?这是因为一个 Symbol 是无法被遍历的,如图:

    在这里插入图片描述

  7. 那如果获取这个 Symbol 呢?如图:

    在这里插入图片描述

  8. 具体是不是可以呢,我们写一些代码测试一下,如图:

    在这里插入图片描述

  9. 所以我们现在需要针对 Symbol 为key时进行一些特殊的处理,如下:

    function deepClone(value) {if (typeof value === 'symbol') {return Symbol(value.description)}if (typeof value === 'function') {return value}if (!isObject(value)) {return value}const newObj = Array.isArray(value) ? [] : {}for (let key in value) {newObj[key] = isObject(value) ? deepClone(value[key]) : value[key]}// 获取对象上所有的为 Symbol 类型的keyconst symKeys = Object.getOwnPropertySymbols(value)for (const sk of symKeys) {// 赋值调用当前函数即可//  - 至于这个 key 是不是要重新 Symbol 就看自己的需求了newObj[sk] = deepClone(newObj[sk])}return newObj
    }
    
  10. 查看结果,如图:

    在这里插入图片描述

处理 Set

  1. 修改数据如下:

    const obj = {name: '张三',age: 18,set: new Set([1, 2, 3])
    }
    
  2. 拷贝的结果如图:

    在这里插入图片描述

  3. set 变了一个空对象,这肯定不是我们需要的结果,那就还需要对是一个 set 类型的时候进行处理,这个也很简单

    function deepClone(value) {if (value instanceof Set) {return new Set([...value])}if (typeof value === 'symbol') {return Symbol(value.description)}if (typeof value === 'function') {return value}if (!isObject(value)) {return value}const newObj = Array.isArray(value) ? [] : {}for (let key in value) {newObj[key] = isObject(value) ? deepClone(value[key]) : value[key]}const symKeys = Object.getOwnPropertySymbols(value)for (const sk of symKeys) {newObj[sk] = deepClone(newObj[sk])}return newObj
    }
    
  4. 结果如图:

    在这里插入图片描述

  5. 但是这种赋值的方法是存在一些隐患的,比如我们存入的是 set 是对象呢?首先改变数据,我们看一下代码,如下:

    const obj = {name: '张三',age: 18,set: new Set([1, 2, 3]),sets: new Set()
    }const o1 = { a: 1, b: 2 }
    const o2 = { c: 3, d: 4 }
    obj.sets.add(o1)
    obj.sets.add(o2)
    
  6. 我们按照现在的方法看一下克隆的结果,测试代码如下:

    const newObj = deepClone(obj)console.log(newObj)const arr1 = []for (const [key, value] of newObj.sets.entries()) {arr1.push(value)
    }console.log(arr1[0] === o1)arr1[0].a = 100
    console.log(o1)
    
  7. 结果如图:

    在这里插入图片描述

  8. 可以看到 o1 这个对象是受到了影响的,所以我们需要单独对这个赋值的过程进行一些处理,如下:

    function deepClone(value) {if (value instanceof Set) {// 创建一个数组来存储值const list = []// 通过 forEach 方法将 Set 中的值复制到数组中value.forEach(item => {// 而这个值通过递归在处理一次list.push(deepClone(item))})// 创建一个新的 Set 对象,并将数组中的值作为参数传入return new Set(list)}if (typeof value === 'symbol') {return Symbol(value.description)}if (typeof value === 'function') {return value}if (!isObject(value)) {return value}const newObj = Array.isArray(value) ? [] : {}for (let key in value) {newObj[key] = isObject(value) ? deepClone(value[key]) : value[key]}const symKeys = Object.getOwnPropertySymbols(value)for (const sk of symKeys) {newObj[sk] = deepClone(newObj[sk])}return newObj
    }
    
  9. 现在我们再看一下处理的结果,如图:

    在这里插入图片描述

处理 Map

  1. 修改的数据如下:

    const obj = {name: '张三',age: 18,map: new Map()
    }const o1 = { a: 1, b: 2 }
    const o2 = { c: 3, d: 4 }
    const o3 = { name: 'ls', age: 22 }obj.map.set('o1', o1)
    obj.map.set(o2, o3)
    
  2. 如果不处理 map,看看打印的结果,如图:

    在这里插入图片描述

  3. 这个结果一样也不是我们所期望的,但是 set 和 map 处理的方式其实都是差不多的,所以我就直接展示代码了,如下:

    function deepClone(value) {if (value instanceof Set) {const list = []value.forEach(item => {list.push(deepClone(item))})return new Set(list)}// 处理 mapif (value instanceof Map) {const myMap = new Map()for (const [key, _val] of value) {// 这里的 key 是不是需要进行再次的递归处理就取决于自己的需求了//  - 一般是不需要再次做额外的处理的const newValue = deepClone(_val)myMap.set(key, newValue)}return myMap}if (value instanceof Map) {const list = []value.forEach(item => {list.push(deepClone(item))})return new Map(list)}if (typeof value === 'symbol') {return Symbol(value.description)}if (typeof value === 'function') {return value}if (!isObject(value)) {return value}const newObj = Array.isArray(value) ? [] : {}for (let key in value) {newObj[key] = isObject(value) ? deepClone(value[key]) : value[key]}const symKeys = Object.getOwnPropertySymbols(value)for (const sk of symKeys) {newObj[sk] = deepClone(newObj[sk])}return newObj
    }
    
  4. 结果如图:

    在这里插入图片描述

  5. 测试结果,如下:

    console.log(newObj.map.get('o1') === obj.map.get('o1')) // false
    

处理 循环引用

  1. 修改数据如下:

    const obj = {name: '张三',age: 18
    }// my 属性引用自身
    obj.my = obj
    
  2. 查看输出的结果,如下:

    在这里插入图片描述

  3. 直接就报了栈溢出的错误,这就是每次递归拷贝的都是 obj 本身,而每个 obj 都具备 my 属性指向自身,递归没有终止条件,自然就会报错

  4. 这个 my 是通过 obj.my = obj 实现的,那么是不是表示我们在创建了一个 newObj 的时候,也只是需要把 newObj 这个对象自身赋值给 newObj.my = newObj 就可以了,而不需要一直的拷贝

  5. 为了实现这个,我们就需要来分析一下了,怎么完成 newObj.my = newObj 这一步操作,要完成这个操作首先我们就要能够获取到 newObj 这个对象,这是第一点;第二个条件就是我们还需要能够保存最开始克隆的原始对象 obj,而能够实现这一点的要求的,我们就可以联想到 map 或者 weakmap,这里保证 obj 存储的时候就是弱引用,而不会外界需要销毁的时候导致无法销毁,使用 weakpack 会更加的合适

  6. 所以我们应该有这么一个操作,手动创建一个 weakmap,然后再 deepClone 第一次创建了 newObj 的时候,就进行存储,wm.set(obj, newObj),而当存在一个这样的数据的时候,我们在就可以在 deepClone 方法上加上一个条件,if(当vm中存在obj这个k的时) { return 就返回存储的 newObj 这个值 }

  7. 梳理了过程,就可以回到具体的代码实现,如下:

    // 全局创建 WeakMap
    const wm = new WeakMap()function deepClone(value) {if (value instanceof Set) {const list = []value.forEach(item => {list.push(deepClone(item))})return new Set(list)}if (value instanceof Map) {const myMap = new Map()for (const [key, _val] of value) {const newValue = deepClone(_val)myMap.set(key, newValue)}return myMap}if (value instanceof Map) {const list = []value.forEach(item => {list.push(deepClone(item))})return new Map(list)}if (typeof value === 'symbol') {return Symbol(value.description)}if (typeof value === 'function') {return value}if (!isObject(value)) {return value}// 当拷贝到 obj.my 这个属性的时候,由于 obj.my 的值就是 obj 本身//  - 所以此时传入的值就是 obj,而其他属性如果没有引用自身的话,就不会存在//  - 就可以通过判断 weakmap 中是否存在这个数据,如果存在就直接返回 weakmap 中一开始存储的值if (wm.has(value)) {return wm.get(value)}const newObj = Array.isArray(value) ? [] : {}// 创建 newObj 的时候,以需要克隆的初始值作为 key,newObj 作为 valuewm.set(value, newObj)for (let key in value) {newObj[key] = isObject(value) ? deepClone(value[key]) : value[key]}const symKeys = Object.getOwnPropertySymbols(value)for (const sk of symKeys) {newObj[sk] = deepClone(newObj[sk])}return newObj
    }
    
  8. 结果如图:

    在这里插入图片描述

  9. 基于此我们就可以实现 newObj.my.my.my… 的操作,如图:

    在这里插入图片描述

  10. 而进行到这一步,还有一个需要的地方,由于我们这个 weakmap 是全局的,就会导致如果在实际的使用中多次调用 deepClone 这个方法的时候,weakmap 这个里面的数据就会越来越多,而实际当完成拷贝的时候,这个数据就不用存在了

  11. 因此我们可以改写一下,如下:

    // 通过参数实现创建 weakmap,只要没有传递,就会自动创建,而如果没有传递了则不会创建,就会使用传递的 weakmap
    function deepClone(value, wm = new WeakMap()) {if (value instanceof Set) {const list = []value.forEach(item => {// 传递 weakmaplist.push(deepClone(item, wm))})return new Set(list)}if (value instanceof Map) {const myMap = new Map()for (const [key, _val] of value) {// 传递 weakmapconst newValue = deepClone(_val, wm)myMap.set(key, newValue)}return myMap}if (value instanceof Map) {const list = []value.forEach(item => {// 传递 weakmaplist.push(deepClone(item, wm))})return new Map(list)}if (typeof value === 'symbol') {return Symbol(value.description)}if (typeof value === 'function') {return value}if (!isObject(value)) {return value}if (wm.has(value)) {return wm.get(value)}const newObj = Array.isArray(value) ? [] : {}wm.set(value, newObj)for (let key in value) {// 同时在内部调用的时候,为了防止后续调用在创建 weakmap,我们在这里调用的时候就要把第一次执行 deepClone 创建的 weakmap 传递进去newObj[key] = isObject(value) ? deepClone(value[key], wm) : value[key]}const symKeys = Object.getOwnPropertySymbols(value)for (const sk of symKeys) {// 传递 weakmapnewObj[sk] = deepClone(newObj[sk], wm)}return newObj
    }
    
  12. 结果如图:

    在这里插入图片描述

  13. 在效果上也是没有问题的

结语-源码

我在测试中都是单独每一项数据进行测试的,是为了更好的观测,实际一个对象都包含这些数据的话也都是 ok的,需要的话可以自己测试,而且写下来就会发现,其实逻辑都是差不多的,可以根据你实际的情况进行增加或者减免,在日常的开发中使用 JSON 序列化一般也可满足我们的需求,不过知道不用和不知道还是存在本质的区别的,可能现在有些你学习的技术没有实际的意义,但是只有积累的足够多的时候你才能完成一些本质上的突破

很多事情不是因为看到了希望才去坚持,而是因为坚持了才能看到希望

function deepClone(value, wm = new WeakMap()) {if (value instanceof Set) {const list = []value.forEach(item => {list.push(deepClone(item, wm))})return new Set(list)}if (value instanceof Map) {const myMap = new Map()for (const [key, _val] of value) {const newValue = deepClone(_val, wm)myMap.set(key, newValue)}return myMap}if (value instanceof Map) {const list = []value.forEach(item => {list.push(deepClone(item, wm))})return new Map(list)}if (typeof value === 'symbol') {return Symbol(value.description)}if (typeof value === 'function') {return value}if (!isObject(value)) {return value}if (wm.has(value)) {return wm.get(value)}const newObj = Array.isArray(value) ? [] : {}wm.set(value, newObj)for (let key in value) {newObj[key] = isObject(value) ? deepClone(value[key], wm) : value[key]}const symKeys = Object.getOwnPropertySymbols(value)for (const sk of symKeys) {newObj[sk] = deepClone(newObj[sk], wm)}return newObj
}

相关文章:

使用递归实现深拷贝

文章目录 为什么要使用递归什么深拷贝具体实现基础实现处理 函数处理 Symbol处理 Set处理 Map处理 循环引用 结语-源码 为什么要使用递归什么深拷贝 我们知道在 JavaScript 中可以通过使用JSON序列化来完成深拷贝,但是这种方法存在一些缺陷,比如对于函数…...

工程(十七)——自己数据集跑R2live

博主创建了一个科研互助群Q:772356582,欢迎大家加入讨论。 r2live是比较早的算法,编译过程有很多问题,通过以下两个博客可以解决 编译R2LIVE问题&解决方法-CSDN博客 r2live process has died 问题解决了_required process …...

【python高级用法】迭代器、生成器、装饰器、闭包

迭代器 可迭代对象:可以使用for循环来遍历的,可以使用isinstance()来测试。 迭代器:同时实现了__iter__()方法和__next__()方法,可以使用isinstance()方法来测试是否是迭代器对象 from collections.abc import Iterable, Iterat…...

Nx市工业数据洞察:Flask、MySQL、Echarts的可视化之旅

Nx市工业数据洞察:Flask、MySQL、Echarts的可视化之旅 背景数据集来源技术选型功能介绍创新点总结 背景 随着工业化的不断发展,Nx市工业数据的收集和分析变得愈发重要。本博客将介绍如何利用Flask、MySQL和Echarts等技术,从统计局获取的数据…...

关于正态分布

目录 1.正态分布是什么2.正态分布有什么用途3.如何确定数据服从正态分布 本文简单介绍正态分布的基本概念和用途。 1.正态分布是什么 正态分布,也称为高斯分布,是由德国数学家卡尔弗里德里希高斯在研究测量误差时提出的。他发现许多自然现象和统计数据…...

每日一练(编程题-C/C++)

目录 CSDN每日一练1. 2023/2/27- 一维数组的最大子数组和(类型:数组 难度:中等)2. 2023/4/7 - 小艺照镜子(类型:字符串 难度:困难)3. 2023/4/14 - 最近的回文数(难度:中等)4. 2023/2/1-蛇形矩阵(难度:困难)…...

Unity UnityWebRequest 在Mac上使用报CommectionError

今天是想把前两天写的Demo拿到Mac上打个IPA的完事我发现 在运行时释放游戏资源的时候UnityWebRequest返回的结果不是Success 查看Log发现是 req.result 是CommectionError error是 Cannot connect to destination host 代码如下: UnityWebRequest req UnityWebRequ…...

WorkPlus为企业打造私有化部署IM解决方案

在移动数字化时代,企业面临着如何全面掌控业务和生态的挑战。企业微信、钉钉、飞书、Teams等应用虽然提供了部分解决方案,但无法满足企业的私有化部署需求。此时,WorkPlus作为安全专属的移动数字化平台,被誉为移动应用的“航空母舰…...

QT上位机开发(抽奖软件)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 用抽奖软件抽奖,是一种很常见的抽奖方式。特别是写这篇文章的时候,正好处于2023年12月31日,也是一年中最后一天…...

雨课堂作业整理

第一次作业 1.下列序列是图序列的是( ) A.1,2,2,3,4,4,5 B.1,1,2,2,4,6,6 C.0,0,2&am…...

C#/WPF 只允许一个实例程序运行并将已运行程序置顶

使用用互斥量(System.Threading.Mutex)&#xff1a; 同步基元&#xff0c;它只向一个线程授予对共享资源的独占访问权。在程序启动时候&#xff0c;请求一个互斥体&#xff0c;如果能获取对指定互斥的访问权&#xff0c;就职运行一个实例。 实例代码&#xff1a; /// <…...

【基础】【Python网络爬虫】【1.认识爬虫】什么是爬虫,爬虫分类,爬虫可以做什么

Python网络爬虫基础 认识爬虫1.什么是爬虫2.爬虫可以做什么3.为什么用 Ptyhon 爬虫4.爬虫的分类通用爬虫聚焦爬虫功能爬虫增量式爬虫分布式爬虫 5.爬虫的矛与盾&#xff08;重点&#xff09;6.盗亦有道的君子协议robots7.爬虫合法性探究 认识爬虫 1.什么是爬虫 网络爬虫&…...

【算法与数据结构】860、LeetCode柠檬水找零

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;本题的思路比较简单&#xff0c;首先要保存收到的零钱&#xff0c;其次计算找零&#xff0c;最后分解找…...

「Verilog学习笔记」乘法与位运算

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点&#xff0c;刷题网站用的是牛客网 观察乘数的特点&#xff1a; 1111_1011 1_0000_0000 - 1 - 100 timescale 1ns/1nsmodule dajiang13(input [7:0] A,output [15:0] B);//*************code*********…...

CSS与JavaScript的简单认识

CSS&#xff1a;是一门语言&#xff0c;用于控制网页表现&#xff0c;让页面更好看的。 CSS&#xff08;Cascading Style Sheet&#xff09;&#xff1a;层叠样式表 CSS与html结合的三种方式&#xff1a; 1、内部样式&#xff1a;用style标签&#xff0c;在标签内部定义CSS样式…...

MAC 中多显示器的设置(Parallels Desktop)

目录 一、硬件列表&#xff1a; 二、线路连接&#xff1a; 三、软件设置&#xff1a; 1. 设置显示器排列位置及显示参数 2. 分别设置外接显示器为&#xff1a;扩展显示器&#xff0c;内建显示器为主显示器 3. 设置Parallels Desktop屏幕参数 四、结果 一、硬件列表&a…...

迁移到云原生:如何使用微服务迁移应用程序

企业遇到大规模部署和监督生产中的应用程序的任务。幸运的是&#xff0c;我们可以使用大量技术和工具。然而&#xff0c;从传统的&#xff0c;整体的结构转变为云态一个人提出了自己的障碍。在这里&#xff0c;您会发现将应用程序从整体设置转移到基于微服务的体系结构时要进行…...

kafka 的零拷贝原理

文章目录 kafka 的零拷贝原理 今天来跟大家聊聊kafka的零拷贝原理是什么&#xff1f; kafka 的零拷贝原理 零拷贝是一种减少数据拷贝的机制&#xff0c;能够有效提升数据的效率&#xff1b;   在实际应用中&#xff0c;如果我们需要把磁盘中的某个文件内容发送到远程服务器上…...

华为云Stack 8.X流量模型分析(五)

六、EIP流量模型分析 ​ 弹性公网IP&#xff08;Elastic IP&#xff0c;简称EIP&#xff09;提供独立的公网IP资源&#xff0c;包括公网IP地址与公网出口带宽服务。如果资源只配置了私网IP&#xff0c;则无法直接访问Internet&#xff0c;为资源配置弹性公网IP后&#xff0c;可…...

学习动态规划解决不同路径、最小路径和、打家劫舍、打家劫舍iii

学习动态规划|不同路径、最小路径和、打家劫舍、打家劫舍iii 62 不同路径 动态规划&#xff0c;dp[i][j]表示从左上角到(i,j)的路径数量dp[i][j] dp[i-1][j] dp[i][j-1] import java.util.Arrays;/*** 路径数量* 动态规划&#xff0c;dp[i][j]表示从左上角到(i,j)的路径数量…...

nodejs微信小程序+python+PHP特困救助供养信息管理系统-计算机毕业设计推荐

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…...

Vue(二):计算属性与 watch 监听器

03. Vue 指令拓展 3.1 指令修饰符 可以通过 . 来指明一些指令的后缀&#xff0c;不同的后缀中封装了不同的操作&#xff0c;可以帮助我们简化代码&#xff0c;比如之前使用过的监听 enter 键的弹起&#xff0c;我们需要操作事件对象&#xff0c;来检测用户使用了哪个键&#…...

25、WEB攻防——通用漏洞SQL读写注入MYSQLMSSQLPostgreSQL

文章目录 Mysql-root高权限读写注入PostgreSQL——dba高权限读写注入Mssql-sa高权限读写注入 Access无高权限注入点——只能猜解&#xff0c;而且是暴力猜解&#xff1b; MYSQL&#xff0c;PostgreSQL&#xff0c;MSSQL(SQL server)高权限注入点——可升级读写&#xff08;文件…...

【第5期】前端Vue使用Proxy+Vuex(store、mutations、actions)跨域调通本地后端接口

本期简介 本期要点 本地开发前后端如何跨域调用全局请求、响应处理拦截器处理封装HTTP请求模块编写API请求映射到后端API数据的状态管理 一、 本地开发前后端如何跨域调用 众所周知&#xff0c;只要前端和后端的域名或端口不一样&#xff0c;就存在跨域访问&#xff0c;例如&…...

在Visual Studio(VS)编译器中,Release和Debug区别

一、 优化级别 1、Debug&#xff08;调试&#xff09; 在Debug模式下&#xff0c;编译器不会对代码进行优化&#xff0c;而是专注于生成易于调试的代码。这使得开发者可以在调试过程中更直观地跟踪变量的值和程序的执行流程。 2、Release&#xff08;发布&#xff09; 在Relea…...

子网划分问题(实战超详解)_主机分配地址

文章目录: 子网划分的核心思想 第一步,考虑借几位作为子网号 第二步,确定子网的网络地址 第三步,明确网络地址,广播地址,可用IP地址范围 一些可能出现的疑问 实战 题目一 子网划分的核心思想 网络号不变,借用主机号来产生新的网络 划分前的网络:网络号主机号 划分后的网络:原网…...

【QT】单例模式,Q_GLOBAL_STATIC 宏的使用和使用静态成员函数,eg:{简单的日志记录器}

简单的日志记录器为例 。 创建一个Logger类&#xff0c;该类负责记录应用程序的日志消息 使用 Q_GLOBAL_STATIC 宏 解析&#xff1a;Q_GLOBAL_STATIC 是一个 Qt 宏&#xff0c;用于创建全局静态实例。它确保在需要时只创建一次实例&#xff0c;而不管该实例是在哪个线程中创建…...

利用小红书笔记详情API:构建高效的内容创作与运营体系

随着社交媒体的兴起&#xff0c;小红书作为国内知名的内容分享平台&#xff0c;吸引了大量用户和内容创作者。为了更好地获取小红书上的优质内容&#xff0c;许多企业和开发者选择使用小红书笔记详情API。本文将探讨如何利用该API构建高效的内容创作与运营体系。 一、小红书笔记…...

【K8S 二进制部署】部署单Master Kurbernetes集群

目录 一、基本架构和系统初始化 1、集群架构&#xff1a; 2、操作系统初始化配置&#xff1a; 2.1、关闭防火墙和安全机制&#xff1a; 2.2、关闭swap 2.3、根据规划设置主机名 2.4、三台主机全部互相映射 2.5、调整内核参数 3、时间同步&#xff08;所有节点时间必须同…...

vue中常见的指令

简单介绍一下常见的vue中用到的指令 v-on 指定当前的事件&#xff0c;语法糖为&#xff0c;如例子所示&#xff0c;指定按钮的事件为addCounter&#xff0c;点击会使变量counter 1 <!DOCTYPE html> <html><head><meta charset"utf-8" />…...

温州 网站建设/天津推广的平台

1. 文件系统 文件系统是一个对存储设备上的数据进行组织的机制&#xff0c;这种机制便于用户和操作系统进行交互。文件系统有对应的物理空间&#xff0c;Linux下每个分区都是一个文件系统&#xff0c;都有自己的目录结构。文件系统包含着文件中的数据、文件系统的结构&#xff…...

网站 制作公司/2023广东最新疫情

写该系列文章的初衷是“让每位前端工程师掌握高频知识点&#xff0c;为工作助力”。这是前端百题斩的第8-9斩&#xff0c;希望朋友们关注公众号“执鸢者”&#xff0c;用知识武装自己的头脑。8. 代码的执行过程下面将以V8为例&#xff0c;来阐述一下JavaScript代码的执行过程&a…...

做网站工作职责/app代理推广平台

市场环境竞争愈发激烈的今天&#xff0c;电销型企业降本增效的需求越来越强大。 电销外呼系统就很好地从企业手中接过这份需求。电销外呼系统可以满足各行业企业的拨号外呼需求&#xff0c;通过系统智能化高效地触达客户&#xff1b;通过大数据的运用、AI自我学习等人工智能技…...

电商网站入口/如何利用互联网宣传与推广

当产品要发布之前,都会进行反复的测试输入,比如:测试按键,遥控,触摸等等. 当出现bug时,就还需要不停地找规律,修改程序,直到修复成功,会显的非常麻烦 答: 可以通过之前在35.Linux-分析并制作环形缓冲区里学的,使用myprintf()保存到环形缓冲区. 当出错时,直接可以通过缓冲区里的…...

网站建设需要哪些工作室/百度快照官网

####原理########mysql 的数据同步&#xff0c;在 mysql 官方网站文档上&#xff0c;叫 replication 字面是重作的意思&#xff0c;意译就是同步了。mysql 为了实现 replication 必须打开 bin-log 项&#xff0c;也是打开二进制的 mysql 日志记录选项。mysql 的 bin log 二进制…...

有了网站源码怎么做app/北京seo优化多少钱

matlab在图片上画框 之前写过一个MATLAB在图片上画框的代码&#xff0c; http://blog.csdn.net/carson2005/article/details/17262811 最近使用后发现效果不好&#xff0c;貌似有个bug&#xff0c;这里做更改&#xff0c; [cpp] view plaincopyfunction [state result] draw_r…...