js-手写题(2)

发布于:2025-02-10 ⋅ 阅读:(43) ⋅ 点赞:(0)

实现浅拷贝

    // 浅拷贝:拷贝的是对象的引用,而不是对象本身。如果原对象的某些属性是复杂数据类型(如数组、对象),浅拷贝不会拷贝这些嵌套的对象,而是拷贝其引用地址,导致新旧对象共享同一个嵌套对象的引用
    // 浅拷贝指的是拷贝对象或数组的第一层内容,但对于对象内部的引用类型,拷贝的是他们的引用地址,而不是他们本身的深层数据

    // 浅拷贝常用方法:
    // 1. 对于引用数据类型的数据,直接赋值
    // 2. Object.assign()
    // 3. 扩展运算符...
    // 4. 数组方法实现【数组浅拷贝】
    //    4.1 Array.prototype.slice
    //    4.2 Array.prototype.concat
    // 5. 手写实现浅拷贝


    // 1. 直接赋值
    let test1 = 1
    let test2 = { name: 'lisi' }

    let test1_1 = test1
    let test2_1 = test2

    test1_1 = 2
    test2_1.name = 'zhangsan'

    console.log(test1) // 1
    console.log(test1_1) // 2
    console.log(test2) // { name: 'zhangsan' }
    console.log(test2_1) // { name: 'zhangsan' }


    // 2. Object.assign() 该方法只会拷贝对象的可枚举属性
    // 注意:
    // 1. 如果目标对象和源对象有同名属性,或者多个源对象有同名属性,则后面的属性会覆盖前面的属性
    // 2. 第一个参数不能为 null 或 undefined\

    let target = { a: 1 } // 目标对象
    let object1 = { b: 2 } // 源对象
    let object2 = { c: 3 } // 源对象
    Object.assign(target, object1, object2) 
    console.log(target) // { a:1, b:2, c:3 }

    let target = { a: 1 } // 目标对象
    let object1 = { a: 2 } // 源对象
    let object2 = { a: 4, c: 3 } // 源对象
    Object.assign(target, object1, object2) 
    console.log(target) // { a:4, c:3 }

    let target = { a: 1 } // 目标对象
    Object.assign(target) 
    console.log(target) // { a:1 }
    console.log(typeof target) // object

    let target = 111 // 目标对象
    Object.assign(target)
    console.log(target) // 111
    console.log(typeof target) // number   


    // 3. 扩展运算符...
    let obj1 = {
      a: 1,
      b: {
        c: 1
      }
    }
    let obj2 = {...obj1}
    obj2.a = 2
    obj2.b.c = 2
    console.log(obj1) // { a:1, b: { c:2 } }
    console.log(obj2) // { a:1, b: { c:1 } }

    // 4.   数组方法实现数组浅拷贝
    // 4.1  Array.prototype.slice
    let arr = [1,2,3,4]
    let copy = arr.slice()
    console.log(copy)
    console.log(copy === arr)


    let arr = [1, true, null, { name: 'lisi'}]
    let copy = arr.slice()
    console.log(arr) // [1, true, null, { name: 'lisi'}]
    console.log(copy) // [1, true, null, { name: 'lisi'}]
    copy[3].name = 'zhangsan'
    console.log(arr) // [1, true, null, { name: 'zhangsan'}]
    console.log(copy) // [1, true, null, { name: 'zhangsan'}]


    // 4.2 Array.prototype.concat
    let arr = [1,2,3,4]
    let copy = arr.concat()
    console.log(copy) // [1,2,3,4]
    console.log(arr === copy) // false

    let arr = [1, true, null, { name: 'lisi' }]
    let copy = arr.concat()
    console.log(arr) // [1, true, null, { name: 'lisi'}]
    console.log(copy) // [1, true, null, { name: 'lisi'}]
    copy[3].name = 'zhangsan'
    console.log(arr) // [1, true, null, { name: 'zhangsan'}]
    console.log(copy) // [1, true, null, { name: 'zhangsan'}]


    // 5. 手写实现浅拷贝
    function shallowCopy(object) {
      if (!object || typeof object !== 'object') {
        return console.error('只拷贝对象类型的数据')
      }

      let newObject = Array.isArray(object) ? [] : {}

      // 遍历object,并且判断是 object 的属性才拷贝
      // 如果object是数组,key会是数组的索引,逐个拷贝元素
      // 如果object是对象,key会是对象的属性名,逐个拷贝属性
      for (let key in object) { // 遍历object对象的所有属性名
        console.log('key=', key)
        if (object.hasOwnProperty(key)) { // 确保属性是对象本身的属性,而不是继承自原型链上的属性,这样做是为了排除掉原型链上的属性(如Object.prototype上的属性)
          newObject[key] = object[key]
        }
      }

      return newObject
    }

    let obj = {
      a: 1,
      b: 2,
      c: {
        d: 3
      }
    }
    let copy = shallowCopy(obj)
    console.log(copy) // {a:1,b:2,c:{d:3}}
    copy.a = 10
    copy.c.d = 30
    console.log(obj) // {a:1,b:2,c:{d:30}}
    console.log(copy) // {a:10,b:2,c:{d:30}}

    let arr = [1, 2, { name: 'lisi' }]
    let copy = shallowCopy(arr)
    console.log(copy) // [1, 2, { name: 'lisi' }]
    copy[0] = 10
    copy[2].name = 'zhangsan'
    console.log(arr) // [1, 2, { name: 'zhangsan' }]
    console.log(copy) // [10, 2, { name: 'zhangsan' }]

    // 继承属性的情况
    let obj = Object.create({ name: 'jicheng'} )
    obj.a = 1
    const copy = shallowCopy(obj)
    console.log(copy) // {a:1}

实现深拷贝

    //  1. JSON.stringfy()
    // 注意:如果拷贝的对象中有函数、undefined、symbol的话,使用该方法后复制得到的对象中不会有这些属性
    let obj = {
      a:0,
      b: {
        c: 1
      }
    }
    let copy = JSON.parse(JSON.stringify(obj))
    console.log(copy)
    copy.b.c = 100
    console.log(obj) // {a:0,b:{c:1}}
    console.log(copy) // {a:0,b:{c:100}}

    let obj = {
      a: function() {
        console.log(111)
      },
      b: undefined,
      c: Symbol(),  
      d: {
        e: 1
      },
      f: 111,
      g: null
    }
    let copy = JSON.parse(JSON.stringify(obj))
    console.log(copy) // {d:{e:1}, f:111, g: null}



    // 2. lodash的._cloneDeep方法
    // 假设已经安装了lodash库
    let _ = require('lodash')
    let obj = {
      a:1,
      b: null,
      c: undefined,
      d: function() {
        console.log(222)
      },
      e: Symbol,
      f: {
        h: {
          i: 100
        }
      }
    }
    let copy = _.cloneDeep(obj)
    console.log(obj.f.h === copy.f.h) // false


    // 3. 手写实现深拷贝函数(需要考虑循环引用)
    function deepCopy(value, map = new WeakMap()) {
      // 处理基本数据类型和null
      if (!value || typeof value !== 'object') {
        return value
      }

      // 如果是已经处理过的对象(防止循环引用),直接返回
      if (map.has(value)) {
        return map.get(value)
      }

      // 处理Date类型
      if (value instanceof Date) {
        return new Date(value)
      }

      // 处理RegExp类型
      if (value instanceof RegExp) {
        return new RegExp()
      }

      // 处理数组
      if (Array.isArray(value)) {
        let arrCopy = []
        map.set(value, arrCopy) // 记录当前数组,防止循环引用
        for (let i = 0; i < value.length; i++) {
          arrCopy[i] = deepCopy(value[i], map)
        }
        return arrCopy
      }

      // 处理普通对象
      let objCopy = {}
      map.set(value, objCopy) // 记录当前对象,防止循环引用
      for (let key in value) {
        if (value.hasOwnProperty(key)) {
          objCopy[key] = deepCopy(value[key], map)
        }
      }
      return objCopy
    }


    let obj = {
      name: 'John',
      age: 30,
      circular: null
    }
    obj.circular = obj // 创建循环引用

    let copy = deepCopy(obj)
    console.log(copy) // { name: 'John', age:30, circular: {...}}
    console.log(copy.circular === copy) // true
    console.log(copy.circular === obj) // false


    let obj = {
      a: 1,
      b: null,
      c: undefined,
      d: function () {
        console.log(222)
      },
      e: Symbol(1),
      f: {
        h: {
          i: 100
        }
      }
    }

    let copy = deepCopy(obj)
    // console.log(copy)
    // console.log(obj.e === copy.e)

    function test(value) {
      return value
    }

    let a = Symbol(1)
    let b = test(a)
    console.log(b)
    console.log(a === b)

实现sleep函数

    function timeout(delay) {
      return new Promise(resolve => {
        setTimeout(resolve, delay)
      })
    }

    // timeout(2000).then(() => {
    //   console.log('2s后执行了')
    // })

    (async function(){
      console.log('开始')
      await timeout(2000).then(() => {
        console.log('2s后执行了')
      })
      console.log(111)
    })()


    // 常见宏任务和微任务
    // 宏任务:setTimeout、setInterval、setImmediate
    // 微任务:promise.then()、promise.catch()、process.nextTick()

手写Object.assign

    Object.prototype.myAssign = function(target, ...sources) {
      if(!target) {
        throw new TypeError('Connot convert undefined or null to object')
      }
      let tar = Object(target) // 将目标对象转换为对象类型

      sources.forEach(source => {
        for(let key in source) {
          if(source.hasOwnProperty(key)) {
            tar[key] = source[key]
          }
        }
      })

      return tar
    }

    let target = {
      a: 1
    }
    let source1 = {
      b:2
    }
    let source2 = {
      a: 100,
      c:2
    }
    let res = Object.myAssign(target, source1, source2)
    console.log(res)
    console.log(target)



    // for...in:用来遍历对象的可枚举属性(包括继承的属性),它会遍历对象中的所有属性名(键),然后可以通过该键来访问属性值
    // for...of:用于遍历可迭代对象(如数组、字符串、Map、Set等),它会遍历对象的值,而不是键

网站公告

今日签到

点亮在社区的每一天
去签到