面试准备之原生JS

一.数据类型

在javascript当中数据类型总共分为两类:基本类型和引用类型;基本类型是有6种分别是:null,undefined,boolean,number,string和symbol(es6新增,表示独一无二的值,具体可以看阮一峰的介绍);引用类型统称为Object对象,主要包括对象,数组.

基本类型和引用类型的区别:

主要是存储位置不一样,基本类型直接存储在栈当中,而引用类型只是把指针存储在栈中,对应的值是存储在堆中的.

数据类型检测方法:

方法1:typeof

除了null,其它都可以检查;

方法2:A(对象) instanceof B(构造函数)

判断这个构造函数的prototype属性, 在不在这个对象的原型链上.。如果在则返回true,否则返回false。比如:console.log([] instanceof Array);//true(检测方法不准确,因为[] instanceof Object)也是true;

方法3:Object.prototype.toString.call(传入要检测的数据)

这个方法是万能的检测数据类型,比如:console.log(Object.prototype.toString.call(null)); //’[object Null]’

方法4:Array.isArray([])

这是数组单独的检测方法,比如:console.log(Array.isArray([]))//true

二.预解析

预解析的规律:

1.把变量的声明提升到当前作用域的最前面,只会提升变量的声明,不会提升赋值;
2.把函数的声明提升到当前作用域的最前面,只会提升函数的声明,不会提升调用;
3.在同一级作用域里存在变量声明和函数声明,优先会提升变量声明;

演示代码1:

var a = 25;
function abc (){
  alert(a);//undefined
  var a = 10;
}
abc();
console.log(a);//25
function a() {
  console.log('aaaaa');//没有调用,不会执行
}
var a = 1;
console.log(a);//1

预解析会变为:

var a;
function abc (){
  var a;
  alert(a);//undefined
  a = 10;
}
function a() {
  console.log('aaaaa');//没有调用,不会打印
}
a = 25;
abc();
console.log(a);//25
a = 1;
console.log(a);//1

演示代码2:(涉及多个赋值的拆解)

f1();
console.log(c);
console.log(b);
console.log(a);
function f1() {
  var a = b = c = 9;
  console.log(a);
  console.log(b);
  console.log(c);
}

预解析后变为:

function f1() {
  //注意多个赋值拆解规律:变量声明第一,最后赋值第二,依次后往前的规律赋值
  var a;
  c=9;
  b=c;
  a=b;

  console.log(a);//9
  console.log(b);//9
  console.log(c);//9
}
f1();
console.log(c);//9
console.log(b);//9
console.log(a);//只有局部f1里才有,全局里没有,直接报错

三.对象

创建对象的方法

引言:我平时项目中一般四种方法比较常用,但我看了<<javascript高级程序设计>>这本书之后发现有9种方法,分别是:

1.字面量方法:

    var person = {
        name: "小明",
        age: 18,
        sayHi: function () {
            console.log("hi");
        }
    }
    console.log(person);

2.new Object()创建方法:

  var person =new Object();
    person.name="小明";
    person.age=18;
    person.sayHi=function(){
        console.log("hi");
        
    }
    console.log(person);

3.工厂模式创建对象方法:(是new Object()创建的升级,实际是封装函数后将对象返回出来)

 function createPerson(name,age,logData){
        var person =new Object();
        person.name=name;
        person.age=age;
        person.sayHi=function(){
            console.log(logData);  
        }
        return person;
    }
    var person=createPerson('小明',18,"hi")
    console.log(person);

4.构造函数方法:

    function Person(name,age,logData){
        
        this.name=name;
        this.age=age;
        this.sayHi=function(){
            console.log(logData);  
        }
        console.log(this);//this是对象本身
    }
    var person=new Person('小明',18,"hi")
    console.log(person);

构造函数写法两个注意点:

a.函数首字母要大写;

b.要使用new关键字;

构造函数new关键字做的四件事:

提示:结合工厂函数和构造函数对比发现

a.new会在内存中用new Object()方法创建一个新的空对象;

b.new会让this指向这个心的空对象;

c.执行构造函数,给这个新的空对象添加属性和方法;

d.最后,new会将有值的新对象进行返回;

注意:

因为new关键字会自动帮我们返回他创建的对象,所以一般在构造函数中是不用写return返回值的, 那万一构造函数中就是写了return返回值,这样看返回值是简单类型还是引用类型,简单类型不影响,如果是复杂类型,那么这个返回值会覆盖原来new关键字自动返回的那个对象,这样使创建的对象直接变为复杂类型值.

5.原型链方法:也是使用了构造函数,但构造函数参数和函数体都是空,我们直接在函数点prototype点加要添加的属性名=值;

    function Person() {}
    Person.prototype.name = "小明";
    Person.prototype.age = 18;
    Person.prototype.sayHi = function () {
        console.log("hi");

    }
    var person = new Person();
    console.log(person);//它会从原型里找到

6.构造函数和原型链组合方法:(实际就是一部分属性写在构造函数里,一部分属性写在原型里)

 function Person(name) {
        this.name = name;
    }
 Person.prototype = {
        constructor: Person, //这里不写也不会受影响
        age: 18,
        sayHi: function () {
            console.log("hi");
        }
    }
    var person = new Person("小明")
    console.log(person);

7.构造函数动态原型方法:(实际构造函数里面加了一个方法判断,判断方法不存在的情况下才加方法)

    function Person(name,age,fnData){
        this.name=name;
        this.age=age;
        if(typeof this.sayHi !='function'){
            Person.prototype.sayHi=function(){
                console.log(fnData);
            }
        }
    }
    var person=new Person('小明',18,"hi");
    console.log(person);

8.寄生构造函数方法:(书中说了跟工厂模式一模一样)
9.稳妥的构造函数方法:(实际就是没有使用new关键字调用,在函数里面用new Object创建对象,在函数里面将对象进行返回)

    function Person(name,age,fnData) {
        var person =new Object()
        person.name=name;
        person.age=age;
        person.sayHi=function(){
            console.log(fnData);
        }
        return person
    }
    var person=Person("小明",18,"hi")
    console.log(person.sayHi());

四.函数内部this指向

首先我们要知道这几个点:

1.javascript是一种弱类型(可以随意赋值类型),动态类型检查(运行时才检查)的语言,这就导致函数内部this指向声明的话是不确定的,只有在调用时才可以确定;

2.所以我们可以总结一句很有用的话:不管函数或者方法是如何声明的,要看这个函数或者方法是最终谁最终点的,这个this就指向谁.

案例:

  //1.普通函数中的this:指向window
    function welcome(){
        console.log(this);
        
    }
    welcome();//window,相当于window.welcome();
    
    // ----------------------------------
    // 2.对象方法中的this:对象点出来,指向这个对象
    var obj={
        name:"小明",
        sayHi:function(){
            console.log(this);
            
        }
    }
    obj.sayHi();//指向obj对象
    // ----------------------------------
    // 对象里的对象,最终谁点出来就是谁
    var p1={
        dog:{
            name:'阿黄',
            sayHi:function(){
                console.log(this);
            }
        }
    }
    console.log(p1.dog.sayHi());//this是dog对象
    // ----------------------------------

    // 3.构造函数里的this,指向构造函数实例化的对象,这个对象值是已经添加对应的属性和值的对象
    function Student(name,age) {
        this.name=name;
        console.log(this);
        this.age=age;
    }
    var s=new Student('小明',18)//打印有小明的对象
       
        
    // ---------------------------
    // 构造函数忘记使用new的话,指向window
    function Student(name,age) {
        this.name=name;
        console.log(this);//没有写new关键字,相当于给window添加属性和方法
        this.age=age;
    }
    var s= Student('小明',18)//打印window

五.修改函数内部this执行的方法(又叫函数的上下文调用模式)

我们可以使用Function.prototype里的call(),apply()和bind()方法,所以只要是函数,都可以使用这个方法.

 	//1.call();
    // 语法:函数名.call(修改后的this指向,参数1,参数2,...)
    // 特点:会调用这个函数
    function getSum(a,b) {
        console.log(a+b);
        console.log(this);    
    }
    // getSum(1,2)//普通调用,this指向window
    getSum.call(obj,1,2)//this指向obj对象了

    // 2.apply();
    // 语法:函数名.apply(修改后的this指向,[参数1,参数2,...]),与call()不一样的是apply方法只能写两个参数,参数2是数组,里面可以写多个要传递的元素
    // 特点:会调用这个函数
	//两个总结:apply方法只能写两个参数,第二个参数是数组,数组里面可以写多个参数;而call方法里可以直接写要传递的多个参数.
     function getSum(a,b) {
        console.log(a+b);
        console.log(this);    
    }
    // getSum(1,2)//普通调用,this指向window
    getSum.apply(obj,[1,2])//this改为了obj对象

    // 3.bind();
    // 语法:函数名.bind(新的this指向对象,参数1,参数2...)
    // 特点:最大特点不会调用,只是返回一个修改了this后并携带参数的函数
    function getSum(a,b) {
        console.log(a+b);
        console.log(this);    
    }
    // getSum.bind(obj)//不会打印,代表不会调用
    var fn=getSum.bind(obj,1,1);
    // console.log(fn);//这里还是添加了默认参数的getSum函数
    fn()//这里调用,打印this是obj对象    


修改上下文注意点:

在这里插入图片描述

call和apply性能对比哪个好一些?

三个以下的参数传递时两者性能差不多,三个以上的参数call方法会比apply好一些,所以工作中最好用call,当要传递的参数刚好是数组时可以直接使用apply,当然也可以用扩展运算符改成call方法也是可以的.

性能测试办法(供参考)
// 性能测试办法
console.time('A');
for(let i=0;i<1000000;i++){
}
console.timeEnd('A');

六.原型链

答:javascript是面向对象的,每一个实例化的对象都有一个proto属性,这个属性指向它的原型对象,同时,这个实例化对象的构造函数有一个属性prototype,与对象的proto属性指向同一个原型,当一个对象在查找一个属性或方法时,它会第一时间看自己有没有,如果自己没有它会根据proto向它的原型进行查找,如果也没有,它会继续像原型的原型里继续查找,直到查到Object.prototype._proto为null,即原型链的终点null,这样就形成了一个完整的原型链.

我对原型图总结了自己的三条经验,记住这三条就很好理解

1.只要是构造函数,它就有prototype属性,同时构造函数也是对象,它就有proto属性;

2.一个实例化的对象对应的proto完整的路线有四条:实例化对象.proto第一条;构造函数.proto第二条;构造函数Function.proto第三条;构造函数Object.proto第四条;

3.Function.proto和Function.prototype指向同一个Function.prototype;

完整的原型链图:

在这里插入图片描述

补充:记住规律对象proto的起始主线有四条:p1.proto;Person.proto;Function.proto,Object.proto;

内置对象(比如下面的Array,Date)的原型链:

在这里插入图片描述

七.继承的方式

1.混入式继承:实际就是遍历父元素的对象,在对象的循环体里将父元素的值赋值给子类即可(sonObj[key]=fatherObj[key]).

    // 混入式
     var wangjianlin = {
       house:{
         price:10000000,
         adress:'洛杉矶'
       },
       car:{
         price:5000000,
         brand:'劳斯莱斯幻影'
       }
     };
     var wangsicong = {
       grilFriends:['豆得儿','雪梨','张馨予','林更新','001号']
     };
     //wangsicong这个对象想拥有wangjianlin这个对象的车和房.继承
     for(var key in wangjianlin){
       wangsicong[key] = wangjianlin[key];
     }
     console.log(wangsicong);//这样就有了父类和自己的属性和方法

2.替换原型式继承:实际就是将父类的值赋值给子类构造函数的原型(即Son.prototype=fatherObj).

    //替换原型的方式.
   var wangjianlin = {
     house:{
       price:10000000,
       adress:'洛杉矶'
     },
     car:{
       price:5000000,
       brand:'劳斯莱斯幻影'
     }
   };

   //渣男构造构造
   function ZhaNan(gfs){
     this.gfs = gfs;
   }
   //每一个渣男都有一个骗女孩子的方法.
   ZhaNan.prototype.huaQian = function () {
     console.log("花钱请吃6块钱麻辣烫...");
   }

   //替换原型继承.(重点操作)
   ZhaNan.prototype = wangjianlin;

   //实例化一个渣男对象.
   var wangsicong = new ZhaNan(['豆得儿','雪梨','张馨予','林更新','001号']);//给自己添加了gfs属性,值是这个数组
   console.log(wangsicong);
   console.log(wangsicong.car);//可以得到得到父对象里的car属性值

3.混合式继承:
实际也是遍历父类对象,在循环体里用父类对象的值赋值给子类构造函数的原型里(即Son.prototype[key]=fatherObj[key]).

    var wangjianlin = {
      house:{
        price:10000000,
        adress:'洛杉矶'
      },
      car:{
        price:5000000,
        brand:'劳斯莱斯幻影'
      }
    };

    //渣男构造构造
    function ZhaNan(gfs){
      this.gfs = gfs;
    }
    //每一个渣男都有一个骗女孩子的方法.
    ZhaNan.prototype.huaQian = function () {
      console.log("花钱请吃6块钱麻辣烫...");
    }

    //混合式继承.
    //这里并没有替换原型,而是给原型增加了属性和方法.
    for(var key in wangjianlin){
      ZhaNan.prototype[key] = wangjianlin[key];
    }

    //实例化一个渣男对象.
    var wangsicong = new ZhaNan(['豆得儿','雪梨','张馨予','林更新','001号']);
    console.log(wangsicong);
    console.log(wangsicong.car);//也可以得到得到父对象里的car属性值

4.class类的继承:
class类的继承的核心在于使用extends表明继承哪个类,并且在子类的构造函数中必须调用super()方法;

    // 父类
    class Parent {
        constructor(props) {
            this.name = props;
            this.house = {
                price: 10000000,
                adress: '洛杉矶'
            };
            this.car = {
                price: 5000000,
                brand: '劳斯莱斯幻影'
            }
        }
        getValue() {
            console.log("我是父类的方法");
        }
    }
    //  子类
    class Child extends Parent {
        constructor(props) {
            console.log(props);//就是传递过来的参数
            super(props);
            this.name = props;
        }
    }
    let child = new Child("小明")
    console.log(child.getValue()); //可以调用父类方法
    console.log(child.car); //得到父亲的car

八.arguments关键字

每一个函数都有一个单独的arguments对象,它只能写在函数里面,它用来获取函数传递过来的所有的实参,如果在函数里面修改了参数,arguments也会变,它是伪数组,没有数组的方法,但有数组的长度属性,所以,作用:可以根据参数的数量来执行不同的业务逻辑.

在这里插入图片描述

九.闭包

定义:父函数A内部有一个子函数B,子函数B调用的时候可以访问到父函数A中的变量,而在全局里的其它人访问不到,那么子函数B就是闭包.

function A() {
    var a=1;
    window.B=function(){
        console.log(a);
    }
}
A();//先执行一下,因为里面要创建里面内容,创建完后一直存在,不会回收
B();//得到1

闭包的意义就是让我们可以间接的访问函数内部的变量;

经典面试题:

 // 经典题目:下面会打印什么?
for(var i=0;i<=5;i++){
    setTimeout( ()=> {
       console.log(i);
    }, 100);
}
//这里会打印六个6,改为0,1,2...5的方法:
// 方法1,改成自执行函数,并将i传递过去,里面用function接收,其余函数体就照常(利用了闭包)
for(var i=0;i<=5;i++){
    //这里可以访问到i
    (function(j){//自执行函数里接收传递过来的i
        setTimeout( ()=> {
       console.log(j);
    }, 100)
    })(i)
}
// 方法2:定时器添加第三个参数并接收即可
for(var i=0;i<=5;i++){
    setTimeout( (j)=> {
       console.log(j);
    }, 100,i);
}
// 方法3:直接用let
for(let i=0;i<=5;i++){
    setTimeout( ()=> {
       console.log(i);
    }, 100);
}

闭包有两个作用:
1.延长函数里面变量的生命周期;
2.提供有限的访问权限;

十.递归

就是函数内部调用自己,同时要有结束条件.
递归应用:

    //1.用递归求1-n之间的整数的累加和. n=5
   //1+2+3+4+5   getSum(n)
   //1+2+3+4  +5
   //1+2+3    +4
   //1+2      +3
   //1        +2

   //如果我们写了一个函数getSum就是用来求1-n之间的整数累加和的.
   //那在求1-n之间累加和的时候, 要先求1- (n-1)之间的累加和,再加上n本身.
   function getSum(n){
     if(n == 1){
       return 1;
     }
     return getSum(n-1) + n;
   }
   console.log(getSum(5));
   //2.求斐波那契数列中第n位的数是多少.
   //1 1 2 3 5 8 13 21 34.....
   //如果我们写了一个函数getFB()就是用来求斐波那契数列中第n是多少的.
   //要求n位是多少,要先求n-1位和n-2位是多少.
   function getFB(n){
     if(n==1 || n==2){
       return 1;
     }
     return arguments.callee(n-1) + getFB(n-2)//arguments.callee就是getFB
   }
   console.log(getFB(10));
   
   //3.递归求页面上所有的元素.
 var list = [];
 function getHDeles(ele){
   var children = ele.children;//元素集合,是arr,这是求出这个ele元素的所有子代.
   for(var i = 0 ; i < children.length; i++){
     var child = children[i]; //children[i]//得到每一个元素
     list.push(child);//把元素的子代存进来.
     //子代也要求子代.调用这个函数求子代
     getHDeles(child);
   }
 }
 //验证一下.
 //求id为father下的所有元素
 var father = document.getElementById("father");
 getHDeles(father);
 console.log(list);
 //求页面上所有的元素.求dom树.
 getHDeles(document);
 // console.log(list);

十一.防抖和节流:

防抖概念:任务频繁触发的情况下,只有任务触发的间隔超过指定间隔的时候,任务才会执行。(意思就是这件事儿需要等待,如果你反复催促,我就重新计时!核心是定时器)
应用场景有:搜索联想、input验证、resize

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>防抖</title>
</head>
<body>
  <button id="debounce">点我防抖!</button>

  <script>
    window.onload = function() {
      // 1、获取这个按钮,并绑定事件
      var myDebounce = document.getElementById("debounce");
      myDebounce.addEventListener("click", debounce(sayDebounce,2000));
    }

    // 2、防抖功能函数,接受传参
    function debounce(fn,delay) {
      // 4、创建一个标记用来存放定时器的返回值
      let timeout = null;
      return function() {//返回一个函数
        // 5、每次当用户点击/输入的时候,把前一个定时器清除(这里是重点)
        clearTimeout(timeout);
        // 6、然后创建一个新的 setTimeout,
        // 这样就能保证点击按钮后的间隔内如果用户还点击了的话,就不会执行 fn 函数
        timeout = setTimeout(() => {
          fn.call(this, arguments);//这句话是调用fn,并把不确定的实参传过去
        }, delay);//这里设置限定时间
      };
    }

    // 3、需要进行防抖的事件处理
    function sayDebounce() {
      // ... 有些需要防抖的工作,在这里执行
      console.log("防抖成功!");
    }

  </script>
</body>
</html>

效果:

img
防抖案例2:切换页面读取内存案例

在这里插入图片描述

节流概念:指定时间间隔内只会执行一次任务。核心概念是时差,时差还在我就直接return这个函数.

应用场景有:scroll、touchmove

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>节流</title>
</head>
<body>

  <button id="throttle">点我节流!</button>

  <script>
    window.onload = function() {
      // 1、获取按钮,绑定点击事件
      var myThrottle = document.getElementById("throttle");
      myThrottle.addEventListener("click", throttle(sayThrottle,3000));
    }

    // 2、节流函数体
    function throttle(fn,delay) {
      // 4、通过闭包保存一个标记
      let canRun = true;
      return function() {
        // 5、在函数开头判断标志是否为 true,不为 true 则中断函数(这里是重点)
        if(!canRun) {
          return;
        }
        // 6、将 canRun 设置为 false,防止执行之前再被执行
        canRun = false;
        // 7、定时器
        setTimeout( () => {
          fn.call(this, arguments);
          // 8、执行完事件(比如调用完接口)之后,重新将这个标志设置为 true
          canRun = true;
        }, delay);
      };
    }

    // 3、需要节流的事件
    function sayThrottle() {
      console.log("节流成功!");
    }

  </script>
</body>
</html>

效果:

img

十二.重绘与回流(也叫重排)

重绘(repaint):当元素样式的改变不影响布局时,浏览器将使用重绘对元素进行更新,此时由于只需要 UI 层面的重新像素绘制,因此损耗较少。
重绘应用场景:改变元素颜色,改变元素背景色…

回流(reflow):又叫重排(layout)。当元素的尺寸、结构或者触发某些属性时,浏览器会重新渲染页面,称为回流。此时,浏览器需要重新经过计算,计算后还需要重新页面布局,因此是较重的操作。
回流应用场景:浏览器窗口大小改变,元素尺寸/位置/内容发生改变,元素字体大小变化,添加或者删除可见的 DOM 元素…

总结:回流必定会触发重绘,重绘不一定会触发回流。重绘的开销较小,回流的代价较高。

那么,在工作中我们要如何避免大量使用重绘与回流呢?:
1.避免频繁操作样式,可汇总后统一一次修改
2.尽量使用 class 进行样式修改,而不是直接操作样式
3.减少 DOM 的操作,可使用字符串一次性插入

十三.浅深拷贝

我们知道对象是引用类型,假设有一个对象A和对象B,将对象A的值赋值给对象B,当你改了任意一个对象的值时,另一个对象的值都会一起变,而我们实际开发过程中也经常会遇到:后端小伙伴需要我将 接口返回的源数据和页面修改后新数据各发一份给它.这就需要用到对象的拷贝了.

1.浅拷贝:只拷贝对象的第一层就叫浅拷贝.
浅拷贝方法:自己写函数,assign,concat,slice,扩展运算符方法这五种
a.自己写浅拷贝

    const obj1 = {
       name: "小明",
       hobby: ["吃饭", "睡觉"]

   };

   function shallowClone(obj) {
       const obj2 = {};
       for (let key in obj) {
           if (obj.hasOwnProperty(key)) {
               obj2[key] = obj1[key];
           }
         
       }
       return obj2;

   }
   const obj2=shallowClone(obj1);
   obj2.name="小张"
  console.log(obj2);//得到一个新的对象
  console.log(obj1);
  js

b.利用Object.assign()方法

const obj1 = {
 username: 'LiangJunrong',
 skill: {
   play: ['basketball', 'computer game'],
   read: 'book',
 },
 girlfriend: ['1 号备胎', '2 号备胎', '3 号备胎'],
};
const obj2 = Object.assign({}, obj1);

console.log(obj2)

c.利用数组的concat()链接方法
concat方法不会改变原有数组,而是返回一个新数组.

const arr1 = [
  1,
  {
    username: 'jsliang',
  },
];

let arr2 = arr1.concat();
console.log(arr2)

d.使用数组的slice()截取方法
slice() 方法原数组不会改变,而是返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝…

const animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];

console.log(animals.slice(2, 4));
// expected output: Array ["camel", "duck"]

e.利用…展开运算符

   const obj1 = {
        name: "小明",
        hobby: ["吃饭", "睡觉"]

    };
    const obj2 = { ...obj1}
    console.log(obj2); //得到一个新的对象
    console.log(obj1);

上面都只能拷贝一层,如果要拷贝对象两层或者以上,就要用深拷贝了
深拷贝的方法:
a.使用JSON.parse(JSON.stringyfy(oldObj))方法

const arr1 = [
  1,
  {
    username: 'jsliang',
  },
];

let arr2 = JSON.parse(JSON.stringify(arr1));

该方法的局限性:
1、不能存放函数或者 Undefined,否则会丢失函数或者 Undefined;
2、不要存放时间对象,否则会变成字符串形式;
3、不能存放 RegExp、Error 对象,否则会变成空对象;
4、不能存放 NaN、Infinity、-Infinity,否则会变成 null;
5、……更多请自行填坑,具体来说就是 JavaScript 和 JSON 存在差异,两者不兼容的就会出问题。

b.使用第三方库Lodast中的_cloneDeep(oldObj)方法;

var _ = require('lodash');

const obj1 = [
  1,
  'Hello!',
  { name: 'js1' },
  [
    {
      name: 'js2',
    }
  ],
]
const obj2 = _.cloneDeep(obj1);

c.jQuery的extend()方法

      const obj1 = [
        1,
        'Hello!',
        { name: 'js1' },
        [
          {
            name: 'js2',
          }
        ],
      ]
      const obj2 = {};
      /**
       * @name jQuery深拷贝
       * @description $.extend(deep, target, object1, object2...)
       * @param {Boolean} deep 可选 true 或者 false,默认是 false,所以一般如果需要填写,最好是 true。
       * @param {Object} target 需要存放的位置
       * @param {Object} object 可以有 n 个原数据
       */
      $.extend(true, obj2, obj1);

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章