面試準備之原生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);

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