使用递归实现深拷贝

news2025/2/28 20:00:54

文章目录

    • 为什么要使用递归什么深拷贝
    • 具体实现
      • 基础实现
      • 处理 函数
      • 处理 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 赋值给 newObj
    	for (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) {
        // 判断是否是一个 Symbol
    	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]
    	}
    
    	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 类型的key
    	const 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)
    	}
    
    	// 处理 map
    	if (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 作为 value
    	wm.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 => {
    			// 传递 weakmap
    			list.push(deepClone(item, wm))
    		})
    		return new Set(list)
    	}
    
    	if (value instanceof Map) {
    		const myMap = new Map()
    		for (const [key, _val] of value) {
    			// 传递 weakmap
    			const newValue = deepClone(_val, wm)
    			myMap.set(key, newValue)
    		}
    		return myMap
    	}
    
    	if (value instanceof Map) {
    		const list = []
    		value.forEach(item => {
    			// 传递 weakmap
    			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) {
    		// 同时在内部调用的时候,为了防止后续调用在创建 weakmap,我们在这里调用的时候就要把第一次执行 deepClone 创建的 weakmap 传递进去
    		newObj[key] = isObject(value) ? deepClone(value[key], wm) : value[key]
    	}
    
    	const symKeys = Object.getOwnPropertySymbols(value)
    	for (const sk of symKeys) {
    		// 传递 weakmap
    		newObj[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
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【远程开发】穿越跳板机和CLion远程开发——全面配置流程

文章目录 穿越跳板机配置 ProxyJump 方案Cygwin上的配置 建立 SSH Tunneling 方案 代码映射目录映射方案配置Rsync加速 远程服务器方案(todo) 远程Debug tips:本文讲了两种穿越跳板机的方案(推荐ProxyJump方案),和两种代码映射的方案。实际任选一对搭配即…

VC2019更改文件名称代码

VC2019更改文件名称代码 效果代码 效果 华为手机拍摄的视频默认名称是“VID_20231213_111723”,图片名称是“IMG_20231213_111723”,需要批量将“VID”改为“IMG” 代码 代码(C#): csharpStringBuilder sbnew StringBuilder()…

Vue-Vben-Admin:打造高效中大型项目后台解决方案

Vue-Vben-Admin:打造高效中大型项目后台解决方案 摘要: Vue-Vben-Admin是一个基于Vue3.0、Vite、Ant-Design-Vue和TypeScript的开源项目,旨在为开发中大型项目提供一站式的解决方案。它涵盖了组件封装、实用工具、钩子函数、动态菜单、权限验…

Vue2Element-ui编写可复用增删改查列表思路及实现方式

前提:堂弟在学校学计算机,有一个期末作业,需要做一个简单的UI界面 先看下基本效果 登录页面包含基本信息校验及登录和保存用户信息 登录之后的列表页面显示 由于是多个tab中午没有太多时间就直接复用数据模式 整体代码在270多行左右 列表 新增弹窗 修改弹窗 删除 其实这里…

爬虫详细教程第1天

爬虫详细教程第一天 1.爬虫概述1.1什么是爬虫?1.2爬虫工具——Python1.3爬虫合法吗?1.4爬虫的矛与盾1.4.1反爬机制1.4.2反爬策略1.4.3robots.txt协议 2.爬虫使用的软件2.1使用的开发工具: 3.第一个爬虫4.web请求4.1讲解一下web请求的全部过程4.2页面渲染…

HCIP:rip综合实验

实验要求: 【R1-R2-R3-R4-R5运行RIPV2】 【R6-R7运行RIPV1】 1.使用合理IP地址规划网络,各自创建环回接口 2.R1创建环回 172.16.1.1/24 172.16.2.1/24 172.16.3.1/24 3.要求R3使用R2访问R1环回 4.加快网络收敛,减少路由条目数量,增…

Maven下载和安装的详细教程

文章目录 一、Maven下载和安装1.1 下载 Maven1.2 配置环境变量 参考资料 一、Maven下载和安装 1.1 下载 Maven 打开 Maven 的官方网站Maven – Download Apache Maven,下载最新版本的 Maven 在可选择的版本中,不同版本的区别在于: binary是已经编译过的…

【深入浅出JVM原理及调优】「搭建理论知识框架」全方位带你深度剖析Java线程转储分析的开发指南

这里写目录标题 专栏介绍前提准备面向人群知识脉络分析线程转储线程转储分析的介绍JVM和线程运行机制JVM和中间件之间的软件交互 JVM线程转储Java快照的基本信息内存回收日志线程转储分解概述全线程转储标识符Java EE中间件,第三方和自定义应用程序线程HotSpot VM T…

2024任务驱动Java程序设计讲课提纲

文章目录 为何采用任务驱动?任务驱动Java程序设计课程概述项目一:踏上Java开发之旅任务1:安装配置JDK并开发第一个Java程序1、安装JDK2、配置JDK环境变量3、开发第一个Java程序 任务2:搭建Java集成开发环境IntelliJ IDEA1、安装In…

【微服务】springboot整合skywalking使用详解

目录 一、前言 二、SkyWalking介绍 2.1 SkyWalking是什么 2.2 SkyWalking核心功能 2.3 SkyWalking整体架构 2.4 SkyWalking主要工作流程 三、为什么选择SkyWalking 3.1 业务背景 3.2 常见监控工具对比 3.3 为什么选择SkyWalking 3.3.1 代码侵入性极低 3.3.2 功能丰…

计算器写作文

一起来交流编程吧【CSDN app】: http://qm.qq.com/cgi-bin/qm/qr?_wv1027&kx9pL9ccIHGKNLE0CWviAqQ_q6HzxomLW&authKeyVslKe623ptw8VRepda%2Bh0Ttr8Ruz8v%2FBW5HpVzyTWU7ECwpHIZpULMj6qIHYZBVb&noverify0&gro 计算器写作文 题目描述 众所周知&a…

大数据应用领域:数据驱动一切

大数据出现的时间只有十几年,被人们广泛接受并应用只有几年的时间,但就是这短短几年的时间,大数据呈现出爆炸式增长的态势。在各个领域,大数据的身影几乎无处不在。今天我们通过一些大数据典型的应用场景分析,一起来看…

初始Web服务器

一、web服务器 1、什么是web服务器? web服务器就是web项目的容器,我们将开发好的web项目部署到web容器中,才能使用网络中的用户通过浏览器进行访问。 一张图带你了解web服务器有啥作用: 在我的电脑上有一个已经做好的项目&#…

【实用工具】Tmux使用指南

Tmux 三个重要概念 session(会话)、window(窗口)、pane(面板) 一个会话可以有多个窗口,一个窗口可以划分为多个面板 注意在tmux中使用快捷命令的话,需要加上前缀ctrlb 关于session的…

01. BI - Project one, 员工离职预测

文章目录 项目概要数据集处理LRSVM Hi,你好。我是茶桁。 又是开始了一个新的阶段。我不建议一些没基础的同学直接从这里开始,还是要先去之前的课程里补补基础。有的时候即便依葫芦画瓢的把代码写出来了,但是基本原理不清楚。而有的时候&#…

【华为机试】2023年真题B卷(python)-关联子串

一、题目 题目描述: 给定两个字符串str1和str2, str1进行排列组合只要有一个为str2的子串则认为str1是str2的关联子串, 请返回子串在str2的起始位置,若不是关联子串则返回-1。 二、示例 示例1 输入输出示例仅供调试,后…

WSL使用VsCode运行cpp文件

文章目录 缘起主要步骤参考 缘起 今天在阅读《C20设计模式-可复用的面向对象设计方法(原书第2版)》的时候,遇到代码想要运行一下,于是决定使用wsl下的vscode配置cpp的环境。 主要步骤 1.安装gcc和g编译器 打开命令行输入wsl&am…

C++_模板

目录 1、函数模板 1.2 模板原理 2、多个模板参数 3、模板的显示实例化 4、模板的匹配 5、类模板 结语: 前言: 在C中,模板分为函数模板和类模板,而模板的作用就是避免了重复的工作,把原本是程序员要做的重复工作交…

JavaFX:MVC模式学习01-使用PropertyValueFactory将模型与视图绑定

PropertyValueFactory类是“TableColumn cell value factory”,绑定创建列表中的项。示例如下&#xff1a; TableColumn<Person,String> firstNameCol new TableColumn<Person,String>("First Name");firstNameCol.setCellValueFactory(new PropertyVal…

C语言实验4:指针

目录 一、实验要求 二、实验原理 1. 指针的基本概念 1.1 指针的定义 1.2 取地址运算符&#xff08;&&#xff09; 1.3 间接引用运算符&#xff08;*&#xff09; 2. 指针的基本操作 2.1 指针的赋值 2.2 空指针 3. 指针和数组 3.1 数组和指针的关系 3.2 指针和数…