小火柴的博客笔记 小火柴的博客笔记
首页
学过的习
踩过的坑
想过的事
首页
学过的习
踩过的坑
想过的事
  • 学过的习

    • CSS的样式优先级权重
    • 事件冒泡和事件捕获
    • js模块可以通过两种方式对外暴露函数
    • js中的内存泄漏和内存溢出
    • js作用域链
    • 控制台暴露vue
    • es5常用知识总结
    • es6常用知识总结
      • es7常用知识总结
      • js模块化
      • nodeJs学习
      • node搭建服务器
      • webPack学习
      • js原型对象和原型链的理解
      • css阻塞与js阻塞
      • ajax的学习
      • 浏览器的重绘与重排
      • 函数防抖与函数节流学习记录
      • 浏览器储存学习
      • React起步
      • React的基本使用
      • React项目搭建
      • vue中attrs和listeners的使用
      • js的精度问题
      • React的状态管理
      • yarn和npm的对比
      • websocket的使用
    • 想过的事

    • 踩过的坑

    • 前端
    • 学过的习
    小火柴
    目录

    es6常用知识总结

    # 一、let 的使用

    let的作用与var类似, 用于声明一个变量,主要有三个特点: 1、作用域在块内:{} 2、不能够重复声明 3、不会预处理(var会在编译时变量提升)

    
      //console.log(age);// age is not defined
      let age = 12;
      //let age = 13;不能重复声明
      console.log(age);
      let btns = document.getElementsByTagName('button');
      for(let i = 0;i<btns.length;i++){//这里如果是var时,会导致alter出来的结果都是3
              btns[i].onclick = function () {
                  alert(i);
              }
      }
    

    # 二、const的使用

    const的作用是定义一个常量,过去定义常量时,用var声明,虽然全部大写,来表示常量,但是实际上是允许修改的,const声明的常量是不允许修改的。主要特点: 1、作用域在块内:{}, 2、不能够重复声明 3、不会预处理

      const sex = '男';
      console.log(sex);
      //sex = '女';//不能修改
      console.log(sex);
    

    # 三、变量的结构和赋值

    可以用一条语句给多个变量赋值: 1、对象: let {n, a} = {n:'tom', a:12} 2、数组: let [a,b] = [1, 'atguigu'];

    
      let obj = {name : 'kobe', age : 39};
      //对象的解构赋值
      let {age} = obj;
      console.log(age);
    //    let {name, age} = {name : 'kobe', age : 39};
    //    console.log(name, age);
    
      //3. 数组的解构赋值  不经常用
      let arr = ['abc', 23, true,4581];
      let [a, b, c, d] = arr;
      console.log(a, b, c, d);
       let [, , , e] = arr;//找第四个位置的变量
      console.log(e);
      
      function person(p) {//不用解构赋值
          console.log(p.name, p.age);
      }
      person(obj);
    
      function person1({name, age}) {
       console.log(name, age);
      }
      person1(obj);//可以直接传递对象
    

    # 四、模板字符串的拼接

    用来简化字符串的拼接,使用``内...${xxx}...

    
      let obj = {
          name : 'anverson',
          age : 41
      };
      console.log('我叫:' + obj.name + ', 我的年龄是:' + obj.age);
      console.log(`我叫:${obj.name}, 我的年龄是:${obj.age}`);
    

    # 五、对象的简化写法

    1、可以省略的对象中的同名属性值 2、省略方法的function

    
      let x = 3;
      let y = 5;
      //普通写法
    //    let obj = {
    //        x : x,
    //        y : y,
    //        getPoint : function () {
    //            return this.x + this.y
    //        }
    //    };
      //简化的写法
      let obj = {
          x,
          y,
          getPoint(){
              return this.x
          }
      };
      console.log(obj, obj.getPoint());
    

    # 六、箭头函数

    箭头函数主要的作用就是定义匿名函数,多用来定义回调函数。 基本语法: 1、没有参数: () => console.log('x') 2、一个参数: i => i+8 3、大于一个参数: (i,j) => i+j 4、函数体不用大括号: 默认返回结果,可以省略return语句,打印时可以体现。 5、 函数体如果有多个语句, 需要用{}包围,若有需要返回的内容,需要手动返回

       let fun = function () {
           console.log('fun()');
       };
       fun();
       //没有形参,并且函数体只有一条语句,可以改写成
       let fun1 = () => console.log('fun1()');
       fun1();
       console.log(fun1());
      
      
        //一个形参,并且函数体只有一条语句
        let fun2 = x => x;
        console.log(fun2(5));
      
      
        //形参是一个以上
        let fun3 = (x, y) => x + y;
        console.log(fun3(25, 39));//64
      
      
        //函数体有多条语句
        let fun4 = (x, y) => {
            console.log(x, y);
            return x + y;
        };
        console.log(fun4(34, 48));//82,如果不return的话,打印的是没有结果的
      
      
        //setTimeout会将当中的this指向window对象
        setTimeout(() => {
            console.log(this);
        },1000)
      
        function Person() {
           this.obj = {
               showThis : () => {
                   console.log(this);
               }
            }
        }
        let fun5 = new Person();
        fun5.obj.showThis();//Person函数
    

    箭头函数中最重要的就是this的指向问题 官方定义:箭头函数没有自己的this,箭头函数的this不是调用的时候决定的,而是在定义的时候处在的对象就是它的this 判断方法:箭头函数的this看外层的是否有函数,如果有,外层函数的this就是内部箭头函数的this,如果没有,则this是window。

    # 七、点点点运算符

    arguments:主要是在函数内部获取形参的伪数组,内部并没有数组的遍历方法,提供了callee函数来指向函数本身(多用来迭代使用) 点点点运算符: 1、可变参数:用在函数内部,是真正的数组,可以调用数组相关的方法的。 2、扩展运算符:为了拼接数组更加的方便(暂时)

    
        function fun(...values) {
            console.log(arguments);
    //        arguments.forEach(function (item, index) {
    //            console.log(item, index);//是无法使用的
    //        });
            console.log(values);//真正的数组,可以调用数组的方法
            values.forEach(function (item, index) {
                console.log(item, index);
            })
        }
        fun(1,2,3);
        2、
        let arr = [2,3,4,5,6];
        let arr1 = ['abc',...arr, '123132'];
        console.log(arr1);
    

    # 八、形参默认值

    形参默认值是函数有形参的情况下,调用函数时未传参,使用的默认值

      function Point(x=12, y=12) { //如果不传参的情况下,xy默认为12
          this.x = x;
          this.y = y;
      }
      let point = new Point(25, 36);
      console.log(point);
      let p = new Point();
      console.log(p);
    

    # 九、Promise对象

    代表了未来将要发生的事,有三个状态:pending(初始化状态),fullfilled(成功状态), rejected( 失败状态) 作用:可以将异步的操作用同步的流程表达出来,避免了层层嵌套的回调(回调地狱:异步操作执行成功后继续进行回调,会产生多层的回调,出错时,很难定位错误的位置,代码的可读性也不高。)

      
    //创建一个promise实例对象
    let promise = new Promise((resolve, reject) => {
    
        //初始化promise的状态为pending---->初始化状态
        console.log('1111');//同步执行
    
        //启动异步任务
        setTimeout(function () {
            console.log('3333');
            //resolve('atguigu.com');
            //修改promise的状态pending---->fullfilled(成功状态)
            reject('xxxx');
            //修改promise的状态pending----->rejected(失败状态)
        },1000)
    
    });
    promise.then((data) => {
        console.log('成功了。。。' + data);
    }, (error) => {
        console.log('失败了' + error);
    });
    console.log('2222');
    
    最终的打印结果为:1111,2222,3333,失败了
    

    主要应用可以用来封装ajax请求:

    //定义一个用ajax的方法
    function getNews(url) {
      //创建一个promise对象
      let promise = new Promise((resolve, reject) => {
          //初始化promise状态为pending
          
          //启动异步任务
          let request = new XMLHttpRequest();
          
          //设置监听
          request.onreadystatechange = function () {
              if(request.readyState === 4){
                  if(request.status === 200){
                      let news = request.response;
                      resolve(news);
                  }else{
                      reject('请求失败了。。。');
                  }
              }
          };
          
          request.responseType = 'json';//设置返回的数据类型
          request.open("GET", url);//规定请求的方法,创建链接
          request.send();//发送
      })
      return promise;
    }
    

    返回的promise对象可以调用then():

      promise.then(function(
        result => console.log(result),
        errorMsg => alert(errorMsg)
      ))
    

    实际使用:(接上)

      getNews('xxx') .then((data) => {//返回值是一个promise对象
          console.log(data);
          return getNews('xxx'+data.xxx);//可以继续调用
      }, (error) => {
          alert(error);
      })
      .then((data) => {
          console.log(data);
      }, (error) => {
          alert(error);
      })
    

    # 十、Symbol数据类型

    Symbol 是ES6中添加的一种原始的数据类型(String, Number, boolean, null, undefined, 对象),他具有以下特点: 1、Symbol属性对应的值是唯一的,可以解决对象的命名冲突问题 2、Symbol值不能与其他数据进行计算,包括同字符串拼串 3、for in, for of遍历时不会遍历symbol属性。

      let symbol = Symbol();//产生symbol是不需要进行new,可以直接产生。
      //let symbol = Symbol('one');//也可以直接参数使用
      //let symbol2 = Symbol('two');
      console.log(typeof symbol); //symbol
      console.log(symbol);  //Symbol
      
      // 用作对象的属性(唯一)
      let obj = {username: 'kobe', age: 39};
      obj[symbol] = 'hello';
      obj[symbol] = 'symbol';
      console.log(obj);//打印的结果如下
    
      for(let i in obj){
        console.log(i); //遍历的结果中没有Symbol(),使用for of 遍历时会报错
      }
    

    打印的obj对象

    # 十一、Iterator遍历器

    iterator是一种接口机制,为各种不同的数据结构提供统一的访问机制,使满足的数据成员可以按照某种次序排列,并且主要是满足了for..of 的使用。 工作原理: 1.创建一个指针对象,指向数据结构的起始位置。 2.第一次调用next方法,指针自动指向数据结构的第一个成员(第一次调用时才会指向第一个成员!!!) 3.接下来不断调用next方法,指针会一直往后移动,直到指向最后一个成员 4.每调用next方法返回的是一个包含value和done的对象,value表示当前成员的值,done对应的布尔值表示当前的数据的结构是否遍历结束。当遍历结束的时候返回的value值是undefined,done值为true,其他时候的done为false。 目前可以直接使用Iterator遍历器功能的对象为: 1、Array 2、arguments 3、set容器 4、map容器 5、String等

      // 原生测试  数组
      let arr3 = [1, 2, 'kobe', true];
      for(let i of arr3){
        console.log(i);
      }
      // 字符串  string
      let str = 'abcdefg';
      for(let item of str){
        console.log(item);
      }
      
      //模拟Iterator遍历器功能的函数
      function mockIterator(arr) {
        let nextIndex = 0;
        return {
          next: function () {
            return nextIndex<arr.length?{value: arr[nextIndex++], done: false}:{value: undefined, done: true}
          }
        }
      }
      
      let arr = [1,2,3,4,5];
      let iteratorObj = mockIterator(arr);
      console.log(iteratorObj.next());
      console.log(iteratorObj.next());
      console.log(iteratorObj.next());
    

    # 十二、Generator函数

    Generator函数是ES6提供的解决异步编程方案之一,他其实是一个状态机,内部用yield来实现对不同状态的控制。可以生成遍历器对象。可以配合对象的Symbol.iterator属性来使对象具有for of 的使用方法。

    主要特点: 1、function 与函数名之间有一个星号 2、内部用yield表达式来定义不同的状态

      function* generatorExample(){
        let result = yield 'hello';  // 状态值为hello
        yield 'generator'; // 状态值为generator
      }
    

    3、generator函数返回的是指针对象(接iterator),而不会执行函数内部逻辑 4、调用next方法函数内部逻辑开始执行,遇到yield表达式停止,返回{value: yield后的表达式结果/undefined, done: false/true} 5、再次调用next方法会从上一次停止时的yield处开始,直到最后

      function* generatorTest() {
        console.log('函数开始执行');
        yield 'hello';
        console.log('函数暂停后再次启动');
        yield 'generator';
      }
      // 生成遍历器对象
      let Gt = generatorTest();
      // 执行函数,遇到yield后即暂停
      console.log(Gt); // 遍历器对象
      let result = Gt.next(); // 函数执行,遇到yield暂停
      console.log(result); // {value: "hello", done: false}
      result = Gt.next(); // 函数再次启动
      console.log(result); // {value: 'generator', done: false}
      result = Gt.next();
      console.log(result); // {value: undefined, done: true}表示函数内部状态已经遍历完毕
    
    

    6、可以配合对象的Symbol.iterator属性来使对象具有for of 的使用方法。

      // 对象的Symbol.iterator属性;
      let myIterable = {};
      myIterable[Symbol.iterator] = function* () {
        yield 1;
        yield 2;
        yield 4;
      };
      for(let i of myIterable){
        console.log(i);// 1 2 4 
      }
      let obj = [...myIterable];
      console.log(obj); //[1,2,4]
    

    7、yield语句返回结果通常为undefined, 当调用next方法时传参内容会作为启动时yield语句的返回值。

      function getNews(url) {
        $.get(url, function (data) { //使用jQuery的方式发送ajax请求
          console.log(data);
          let commentsUrl = data.commentsUrl;
          let url = 'xxx' + commentsUrl;
          
          // 调用next传参会作为上次暂停是yield的返回值
          sx.next(url); //通过next传参的操作是要在yield的下一步进行的
        })
      }
      
      function* sendXml() {
        // url为next传参进来的数据
        let url = yield getNews('xxx');
        yield getNews(url);
      }
      
      let sx = sendXml();
      sx.next();
        
    

    # 十三、async函数

    async函数是ES2017的内容,主要是解决异步回调的问题,用同步流程来解决异步操作,属于Generator的语法糖

      async function foo(){
        await 异步操作;
        await 异步操作;
      }
    

    主要特点: 1、不需要像Generator去调用next方法,遇到await等待,当前的异步操作完成就往下执行 2、返回的总是Promise对象,可以用then方法进行下一步操作 3、async取代Generator函数的星号*,await取代Generator的yield

      //定义一个异步函数,返回的是Promise对象
      async function timeout(ms) {
          return new Promise(resolve => {
            setTimeout(resolve, ms);
          })
        }
        
        
        async function asyncPrint(value, ms) {
          console.log('函数执行', new Date().toTimeString());
          await timeout(ms);//等待异步函数执行结束,才会继续的向下执行
          console.log('延时时间', new Date().toTimeString());
          console.log(value);
        }
      
      
        asyncPrint('hello async', 2000)
    

    # 十四、class类

    ES6可以通过定义class类实现构造方法,类的继承。可以通过new来创建实例,用extends实现类的继承

    class Person {
    
        //调用类的构造方法
        constructor(name, age){
            this.name = name;
            this.age = age;
        }
        
        //定义一般的方法
        showName(){
            console.log(this.name, this.age);
        }
    }
    let person = new Person('kobe', 39);
    console.log(person, person.showName());//方法在实例的原型上,可以直接调用
    
    
    
    //定义一个子类
    class StrPerson extends Person{
    
        constructor(name, age, salary){
            super(name, age);//调用父类的构造方法,需要将参数传入
            this.salary = salary;
        }
        
        //在子类自身定义方法,相当于重写了父类的方法
        showName(){
            console.log(this.name, this.age, this.salary);
        }
    }
    let str = new StrPerson('weide', 38, 1000000000);
    console.log(str);
    str.showName();
    
    1. 通过class定义类/实现类的继承
    2. 在类中通过constructor定义构造方法
    3. 通过new来创建类的实例
    4. 通过extends来实现类的继承
    5. 通过super调用父类的构造方法
    6. 重写从父类中继承的一般方法
    <script type="text/javascript">
      class Person {
          //调用类的构造方法
          constructor(name, age){
              this.name = name;
              this.age = age;
    
          }
          //定义一般的方法
          showName(){
              console.log(this.name, this.age);
          }
      }
      let person = new Person('kobe', 39);
      console.log(person, person.showName());
    
      //定义一个子类
      class StrPerson extends Person{
          constructor(name, age, salary){
              super(name, age);//调用父类的构造方法
              this.salary = salary;
          }
          showName(){//在子类自身定义方法
              console.log(this.name, this.age, this.salary);
          }
      }
      let str = new StrPerson('weide', 38, 1000000000);
      console.log(str);
      str.showName();
    

    # 十六、字符串方法的扩展

    1. includes(str) : 判断是否包含指定的字符串
    2. startsWith(str) : 判断是否以指定字符串开头
    3. endsWith(str) : 判断是否以指定字符串结尾
    4. repeat(count) : 重复指定次数
      let str = 'abcdefg';
      console.log(str.includes('a'));//true
      console.log(str.includes('h'));//false
      
      //startsWith(str) : 判断是否以指定字符串开头
      console.log(str.startsWith('a'));//true
      console.log(str.startsWith('d'));//false
      
      //endsWith(str) : 判断是否以指定字符串结尾
      console.log(str.endsWith('g'));//true
      console.log(str.endsWith('d'));//false
      
      //repeat(count) : 重复指定次数a
      console.log(str.repeat(5));//abcdefgabcdefgabcdefgabcdefgabcdefg
    

    # 十七、数值方法的拓展

    1. 二进制与八进制数值表示法: 二进制用0b, 八进制用0o
    2. Number.isFinite(i) : 判断是否是有限大的数
    3. Number.isNaN(i) : 判断是否是NaN
    4. Number.isInteger(i) : 判断是否是整数
    5. Number.parseInt(str) : 将字符串转换为对应的数值
    6. Math.trunc(i) : 直接去除小数部分
      console.log(0b1010);//10
      console.log(0o56);//46
      
      //Number.isFinite(i) : 判断是否是有限大的数
      console.log(Number.isFinite(NaN));//false
      console.log(Number.isFinite(5));//true
      
      //Number.isNaN(i) : 判断是否是NaN
      console.log(Number.isNaN(NaN));//true
      console.log(Number.isNaN(5));//falsse
      
      //Number.isInteger(i) : 判断是否是整数
      console.log(Number.isInteger(5.23));//false
      console.log(Number.isInteger(5.0));//true  这也属于整数
      console.log(Number.isInteger(5));//true
      
      //Number.parseInt(str) : 将字符串转换为对应的数值
      console.log(Number.parseInt('123abc'));//123
      console.log(Number.parseInt('a123abc'));//NaN
      
      // Math.trunc(i) : 直接去除小数部分
      console.log(Math.trunc(13.123));//13
    

    # 十八、数组方法的拓展

    1. Array.from(v) : 将伪数组对象或可遍历对象转换为真数组
    2. Array.of(v1, v2, v3) : 将一系列值转换成数组
    3. find(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素
    4. findIndex(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素下标
      //Array.from(v) : 将伪数组对象或可遍历对象转换为真数组
      let btns = document.getElementsByTagName('button');//伪数组
      console.log(btns.length);//3
      Array.from(btns).forEach(function (item, index) {
          console.log(item, index);
      });
      
      //Array.of(v1, v2, v3) : 将一系列值转换成数组
      let arr = Array.of(1, 'abc', true);
      console.log(arr);
      
      //find(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素
      let arr1 = [1,3,5,2,6,7,3];
      let result = arr1.find(function (item, index) {
          return item >3 
      });
      let result2  = arr1.find((item)=>{
            return item > 6     //表达式要返回正确的值
      })
      console.log(result);//5
      
      
      //findIndex(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素下标
      let result1 = arr1.findIndex(function (item, index) {
          return item >3
      });
      console.log(result1);//2
    

    # 十九、对象方法的扩展

    1. Object.is(v1, v2):判断2个数据是否完全相等
    2. Object.assign(target, source1, source2..): 将源对象的属性复制到目标对象上
    3. 直接操作 proto 属性:(隐式原型)
      console.log(Object.is('abc', 'abc'));//true
      
      console.log(NaN == NaN);//false NaN与任何的数都不相等
       console.log(0 == -0);//true
       
       //这两个属于特殊情况,底层在实现的时候用了字符串进行比较
      console.log(Object.is(NaN, NaN));//true  
      console.log(Object.is(0, -0));//false
      
      //Object.assign(target, source1, source2..)
      let obj = {name : 'kobe', age : 39, c: {d: 2}};
      let obj2 = {name : 'jignisgniasjfdi', sex: "test", c: {d: 2}}; 
      let obj1 = {};
      Object.assign(obj1, obj,obj2);
      console.log(obj1, obj1.name);//结果如图所示,注意属性的覆盖
      
      //直接操作 __proto__ 属性
      let obj3 = {name : 'anverson', age : 41};
      let obj4 = {};
      obj4.__proto__ = obj3;
      console.log(obj4, obj4.name, obj4.age);//可以调用原型上面的属性和方法
    

    在这里插入图片描述

    # 二十、set 和 map 的常用方法

    1、set:无序不可重复的多个value的集合体

    • Set() :通个过new Set()生成set 对象
    • Set(array) :通过数组生成set对象
    • add(value)
    • delete(value)
    • has(value)
    • clear()
    • size
      2、 Map容器 : 无序的 key不重复的多个key-value的集合体
    • Map():通过new Map() 生成map对象
    • Map(array):通过数组生成map对象
    • set(key, value)
    • get(key)
    • delete(key)
    • has(key)
    • clear()
    • size
      let set = new Set([1,2,3,4,3,2,1,6]);
      console.log(set);
      set.add('abc');
      console.log(set, set.size);//会将重复的自动删除
      //delete(value)
      set.delete(2);
      console.log(set);
      //has(value)
      console.log(set.has(2));//false
      console.log(set.has(1));//true
      //clear()
      set.clear();
      console.log(set);
      
      
      
      let map = new Map([['abc', 12],[25, 'age']]);//注意是二维数组,数组里面如果出现超过两个,自动去除,就要前两个
      console.log(map);
      map.set('男', '性别');
      console.log(map);
      console.log(map.get(25));//age
      //delete(key)
      map.delete('男');
      console.log(map);
      console.log(map.has('男'));//false
      console.log(map.has('abc'));//true
      map.clear();
      console.log(map);
    

    # 二十一、for...of...使用总结

    for(let value of target) { }:循环遍历

    1. 遍历数组
    2. 遍历Set:可以用来对数组进行去重。
    3. 遍历Map
    4. 遍历字符串
    5. 遍历伪数组

    # 二十二、深度克隆

    数据类型: 数据分为基本的数据类型(String, Number, boolean, Null, Undefined)和对象数据类型。基本数据类型存储的是该对象的实际数据,对象数据类型存储的是该对象在栈中引用,真实的数据存放在堆内存里。 克隆数据的区别: 1、基础数据的复制是可以直接复制的,因为存放的就是实际的数据。 2、对象直接复制时,复制的是对象在栈内存中的引用 常用的拷贝: 1、直接复制,属于 浅拷贝 2、Object.assign() 浅拷贝 3、Array.prototype.concat() 浅拷贝 4、Array.prototype.slice() 浅拷贝 5、JSON.parse(JSON.stringify())深拷贝

    参考老师的自定义深度克隆方法。
    主要:
    1、Object.prototype.toString.call()可以判断是对象还是数组
    2、for ..in..数组是下标,对象是key,都可以用obj[item]来调用
    
    //获取类型
    function getType(target){
    	return Object.prototype.toString.call(target).slice(8,-1)  
    }
    //返回结果
    function getClone(target){
    	let type = this.getType(target);
    	let result;
    	if(type==='Object'){  //为了每次递归时数据不用覆盖
    		result={}
    	}else if(type==='Array'){
    		result=[]
    	}else{
    		return target;
    	}
    	
      for(let i in target){
      	if(this.getType(target[i])==='Object'||this.getType(target[i])==='Array'){
      		result[i]=this.getClone(target[i])  //进行递归
      	}else{
      		result[i]=target[i]
      	}
      }
      return result;
    }
    
    
    let a={
    	x:1,
    	y:2,
    	z:[232,232434]
    }
    b=this.getClone(a)
    b.x=4748;
    b.z[0]=989999999
    console.log(b,b.z)
      
     
    
    //手写深拷贝
    function deepClone(obj) {
      if (typeof obj !== 'object' || obj === null) {
        return obj
      }
    
      let newObj
      if (Array.isArray(obj)) {
        newObj = []
      } else {
        newObj = {}
      }
    
      for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
          newObj[key] = deepClone(obj[key])
        }
      }
    
      return newObj
    }
    

    说明:上面的代码首先判断输入的对象是否为对象或数组,如果不是则直接返回原对象。 否则,根据对象的类型创建一个新的空对象,然后递归遍历对象的每一个属性,并将该属性的值拷贝到新对象上。 注意:该代码仅适用于简单的对象,如果需要复制循环引用的对象,则需要额外处理。对于循环引用的对象,我们可以使用一个 WeakMap 来记录已经处理过的对象,以避免无限递归:

    function deepClone(obj, seen = new WeakMap()) {
      if (typeof obj !== 'object' || obj === null) {
        return obj
      }
    
      if (seen.has(obj)) {
        return seen.get(obj)
      }
    
      let newObj
      if (Array.isArray(obj)) {
        newObj = []
      } else {
        newObj = {}
      }
    
      seen.set(obj, newObj)
    
      for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
          newObj[key] = deepClone(obj[key], seen)
        }
      }
    
      return newObj
    }
    
    #JS
    es5常用知识总结
    es7常用知识总结

    ← es5常用知识总结 es7常用知识总结→

    Theme by Vdoing | Copyright © 2021-2023 X match
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式