亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解JS變量存儲(chǔ)深拷貝和淺拷貝

 更新時(shí)間:2021年05月04日 09:06:34   作者:淺笑·  
這篇文章主要介紹了JS變量存儲(chǔ)深拷貝和淺拷貝,想深入了解JS的同學(xué),可以參考下

變量類型與存儲(chǔ)空間

棧內(nèi)存和堆內(nèi)存

基本數(shù)據(jù)類型

string、number、null、undefined、boolean、symbol(ES6新增) 變量值存放在棧內(nèi)存中,可直接訪問(wèn)和修改變量的值
基本數(shù)據(jù)類型不存在拷貝,好比如說(shuō)你無(wú)法修改數(shù)值1的值

引用類型

Object Function RegExp Math Date 值為對(duì)象,存放在堆內(nèi)存中
在棧內(nèi)存中變量保存的是一個(gè)指針,指向?qū)?yīng)在堆內(nèi)存中的地址。
當(dāng)訪問(wèn)引用類型的時(shí)候,要先從棧中取出該對(duì)象的地址指針,然后再?gòu)亩褍?nèi)存中取得所需的數(shù)據(jù)

圖解存儲(chǔ)空間

let a1 = 0; // 棧內(nèi)存
let a2 = "this is string" // 棧內(nèi)存
let a3 = null; // 棧內(nèi)存
let b = { x: 10 }; // 變量b存在于棧中,{ x: 10 }作為對(duì)象存在于堆中
let c = [1, 2, 3]; // 變量c存在于棧中,[1, 2, 3]作為對(duì)象存在于堆中

引用類型的賦值

let a = { x: 10, y: 20 }
let b = a;
b.x = 5;
console.log(a.x); // 5

深拷貝和淺拷貝

深拷貝

將一個(gè)對(duì)象從內(nèi)存中完整的拷貝一份出來(lái),從堆內(nèi)存中開(kāi)辟一個(gè)新的區(qū)域存放新對(duì)象,且修改新對(duì)象不會(huì)影響原對(duì)象

淺拷貝

淺拷貝是按位拷貝對(duì)象,它會(huì)創(chuàng)建一個(gè)新對(duì)象,這個(gè)對(duì)象有著原始對(duì)象屬性值的一份精確拷貝。如果屬性是基本類型,拷貝的就是基本類型的值;如果屬性是內(nèi)存地址(引用類型),拷貝的就是內(nèi)存地址

對(duì)象的賦值

當(dāng)我們把一個(gè)對(duì)象賦值給一個(gè)新的變量時(shí),賦的其實(shí)是該對(duì)象的在棧中的地址,而不是堆中的數(shù)據(jù)。也就是兩個(gè)對(duì)象指向的是同一個(gè)存儲(chǔ)空間,無(wú)論哪個(gè)對(duì)象發(fā)生改變,其實(shí)都是改變的存儲(chǔ)空間的內(nèi)容,因此,兩個(gè)對(duì)象是聯(lián)動(dòng)的。

三者對(duì)比

淺拷貝的常用的五種方法

Object.assign()

Object.assign() 方法可以把任意多個(gè)的源對(duì)象自身的可枚舉屬性拷貝給目標(biāo)對(duì)象,然后返回目標(biāo)對(duì)象。但是 Object.assign()進(jìn)行的是淺拷貝

Object.assign 會(huì)從左往右遍歷源對(duì)象(sources)的所有屬性,然后用 = 賦值到目標(biāo)對(duì)象(target)

var obj = { a: {a: "kobe", b: 39},b:1 };
        var initalObj = Object.assign({}, obj);
        initalObj.a.a = "wade";
        initalObj.b = 2;
        console.log(obj.a.a); //wade
        console.log(obj.b); //1

擴(kuò)展運(yùn)算符

let obj = {a:1,b:{c:1}}
let obj2 = {...obj};
obj.a=2;
console.log(obj); //{a:2,b:{c:1}}
console.log(obj2); //{a:1,b:{c:1}}

obj.b.c = 2;
console.log(obj); //{a:2,b:{c:2}}
console.log(obj2); //{a:1,b:{c:2}}

Array.prototype.slice

slice() 方法返回一個(gè)新的數(shù)組對(duì)象,這一對(duì)象是一個(gè)由 begin和 end(不包括end)決定的原數(shù)組的淺拷貝。原始數(shù)組的基本類型不會(huì)被改變,引用類型會(huì)被改變。

let arr = [1, 3, {
    username: ' kobe'
    }];
let arr3 = arr.slice();
arr3[0]=0;
arr3[2].username = 'wade'
console.log(arr);

Array.prototype.concat()

let arr = [1, 3, {
    username: 'kobe'
    }];
let arr2=arr.concat();   
arr3[0]=0;
arr2[2].username = 'wade';
console.log(arr);

手寫(xiě)淺拷貝

function shallowCopy(src) {
    var dst = {};
    for (var prop in src) {
        if (src.hasOwnProperty(prop)) {
            dst[prop] = src[prop];
        }
    }
    return dst;
}

深拷貝的常用方法

jsON.parse(jsON.stringify())

通過(guò)JSON.stringify實(shí)現(xiàn)深拷貝有幾點(diǎn)要注意

拷貝的對(duì)象的值中如果有函數(shù),undefined,symbol則經(jīng)過(guò)JSON.stringify()序列化后的JSON字符串中這個(gè)鍵值對(duì)會(huì)消失

無(wú)法拷貝不可枚舉的屬性,無(wú)法拷貝對(duì)象的原型鏈

拷貝Date引用類型會(huì)變成字符串

拷貝RegExp引用類型會(huì)變成空對(duì)象

對(duì)象中含有NaN、Infinity和-Infinity,則序列化的結(jié)果會(huì)變成null

無(wú)法拷貝對(duì)象的循環(huán)應(yīng)用(即obj[key] = obj)

let arr = [1, 3, {
    username: ' kobe'
}];
let arr4 = JSON.parse(JSON.stringify(arr));
arr4[2].username = 'duncan'; 
console.log(arr, arr4)

手寫(xiě)乞丐版深拷貝

首先這個(gè)deepClone函數(shù)并不能復(fù)制不可枚舉的屬性以及Symbol類型

這里只是針對(duì)Object引用類型的值做的循環(huán)迭代,而對(duì)于Array,Date,RegExp,Error,Function引用類型無(wú)法正確拷貝

對(duì)象成環(huán),即循環(huán)引用 (例如:obj1.a = obj)

function clone(target) {
    if (typeof target === 'object') {
        let cloneTarget = Array.isArray(target) ? [] : {};
        for (const key in target) {
            cloneTarget[key] = clone(target[key]);
        }
        return cloneTarget;
    } else {
        return target;
    }
};

皇帝版深拷貝

該實(shí)例來(lái)自ConardLi大佬的github,源地址:https://github.com/ConardLi/

const mapTag = "[object Map]";
    const setTag = "[object Set]";
    const arrayTag = "[object Array]";
    const objectTag = "[object Object]";
    const argsTag = "[object Arguments]";

    const boolTag = "[object Boolean]";
    const dateTag = "[object Date]";
    const numberTag = "[object Number]";
    const stringTag = "[object String]";
    const symbolTag = "[object Symbol]";
    const errorTag = "[object Error]";
    const regexpTag = "[object RegExp]";
    const funcTag = "[object Function]";

    const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag];

    function forEach(array, iteratee) {
      let index = -1;
      const length = array.length;
      while (++index < length) {
        iteratee(array[index], index);
      }
      return array;
    }

    function isObject(target) {
      const type = typeof target;
      return target !== null && (type === "object" || type === "function");
    }

    function getType(target) {
      return Object.prototype.toString.call(target);
    }

    function getInit(target) {
      const Ctor = target.constructor;
      return new Ctor();
    }

    function cloneSymbol(targe) {
      return Object(Symbol.prototype.valueOf.call(targe));
    }

    function cloneReg(targe) {
      const reFlags = /\w*$/;
      const result = new targe.constructor(targe.source, reFlags.exec(targe));
      result.lastIndex = targe.lastIndex;
      return result;
    }

    function cloneFunction(func) {
      const bodyReg = /(?<={)(.|\n)+(?=})/m;
      const paramReg = /(?<=\().+(?=\)\s+{)/;
      const funcString = func.toString();
      if (func.prototype) {
        const param = paramReg.exec(funcString);
        const body = bodyReg.exec(funcString);
        if (body) {
          if (param) {
            const paramArr = param[0].split(",");
            return new Function(...paramArr, body[0]);
          } else {
            return new Function(body[0]);
          }
        } else {
          return null;
        }
      } else {
        return eval(funcString);
      }
    }

    function cloneOtherType(targe, type) {
      const Ctor = targe.constructor;
      switch (type) {
        case boolTag:
        case numberTag:
        case stringTag:
        case errorTag:
        case dateTag:
          return new Ctor(targe);
        case regexpTag:
          return cloneReg(targe);
        case symbolTag:
          return cloneSymbol(targe);
        case funcTag:
          return cloneFunction(targe);
        default:
          return null;
      }
    }

    function clone(target, map = new WeakMap()) {
      // 克隆原始類型
      if (!isObject(target)) {
        return target;
      }

      // 初始化
      const type = getType(target);
      let cloneTarget;
      if (deepTag.includes(type)) {
        cloneTarget = getInit(target, type);
      } else {
        return cloneOtherType(target, type);
      }

      // 防止循環(huán)引用
      if (map.get(target)) {
        return map.get(target);
      }
      map.set(target, cloneTarget);

      // 克隆set
      if (type === setTag) {
        target.forEach(value => {
          cloneTarget.add(clone(value, map));
        });
        return cloneTarget;
      }

      // 克隆map
      if (type === mapTag) {
        target.forEach((value, key) => {
          cloneTarget.set(key, clone(value, map));
        });
        return cloneTarget;
      }

      // 克隆對(duì)象和數(shù)組
      const keys = type === arrayTag ? undefined : Object.keys(target);
      forEach(keys || target, (value, key) => {
        if (keys) {
          key = value;
        }
        cloneTarget[key] = clone(target[key], map);
      });

      return cloneTarget;
    }

    const map = new Map();
    map.set("key", "value");
    map.set("ConardLi", "code秘密花園");

    const set = new Set();
    set.add("ConardLi");
    set.add("code秘密花園");

    const target = {
      field1: 1,
      field2: undefined,
      field3: {
        child: "child"
      },
      field4: [2, 4, 8],
      empty: null,
      map,
      set,
      bool: new Boolean(true),
      num: new Number(2),
      str: new String(2),
      symbol: Object(Symbol(1)),
      date: new Date(),
      reg: /\d+/,
      error: new Error(),
      func1: () => {
        console.log("code秘密花園");
      },
      func2: function(a, b) {
        return a + b;
      }
    };

    const result = clone(target);

    console.log(target);
    console.log(result);

以上就是詳解JS變量存儲(chǔ)深拷貝和淺拷貝的詳細(xì)內(nèi)容,更多關(guān)于JS變量存儲(chǔ)深拷貝和淺拷貝的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • ajax分頁(yè)效果(bootstrap模態(tài)框)

    ajax分頁(yè)效果(bootstrap模態(tài)框)

    這篇文章主要為大家詳細(xì)介紹了ajax分頁(yè)效果的實(shí)現(xiàn)方法,結(jié)合bootstrap模態(tài)框使用 ,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-01-01
  • 一篇文章帶你從零快速上手Rollup

    一篇文章帶你從零快速上手Rollup

    這篇文章主要給大家介紹了如何通過(guò)一篇文章快速?gòu)牧憧焖偕鲜諶ollup的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • JavaScript async/await使用詳解

    JavaScript async/await使用詳解

    任意一個(gè)名稱都是有意義的,先從字面意思來(lái)理解。async 是“異步”的簡(jiǎn)寫(xiě),而 await 可以認(rèn)為是 async wait 的簡(jiǎn)寫(xiě)。所以應(yīng)該很好理解 async 用于申明一個(gè) function 是異步的,而 await 用于等待一個(gè)異步方法執(zhí)行完成
    2022-12-12
  • 使用javascript實(shí)現(xiàn)簡(jiǎn)單的選項(xiàng)卡切換

    使用javascript實(shí)現(xiàn)簡(jiǎn)單的選項(xiàng)卡切換

    本文主要介紹了使用javascript實(shí)現(xiàn)簡(jiǎn)單的選項(xiàng)卡切換的效果,代碼非常的簡(jiǎn)單,兼容性十分棒,這里推薦給小伙伴們。
    2015-01-01
  • Javascript中的this綁定介紹

    Javascript中的this綁定介紹

    在Javascript里,函數(shù)被調(diào)用的時(shí)候,除了接受聲明是定義的形式參數(shù),每一個(gè)函數(shù)還接受兩個(gè)附加的參數(shù):this和arguments。
    2011-09-09
  • JavaScript實(shí)戰(zhàn)之帶收放動(dòng)畫(huà)效果的導(dǎo)航菜單

    JavaScript實(shí)戰(zhàn)之帶收放動(dòng)畫(huà)效果的導(dǎo)航菜單

    這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)戰(zhàn)之帶收放動(dòng)畫(huà)效果的導(dǎo)航菜單,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-08-08
  • JavaScript調(diào)試技巧之console.log()詳解

    JavaScript調(diào)試技巧之console.log()詳解

    對(duì)于JavaScript程序的調(diào)試,相比于alert(),使用console.log()是一種更好的方式,原因在于:alert()函數(shù)會(huì)阻斷JavaScript程序的執(zhí)行,從而造成副作用;而console.log()僅在控制臺(tái)中打印相關(guān)信息,因此不會(huì)造成類似的顧慮
    2014-03-03
  • Layui組件Table綁定行點(diǎn)擊事件和獲取行數(shù)據(jù)的方法

    Layui組件Table綁定行點(diǎn)擊事件和獲取行數(shù)據(jù)的方法

    今天小編就為大家分享一篇Layui組件Table綁定行點(diǎn)擊事件和獲取行數(shù)據(jù)的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-08-08
  • js+canvas實(shí)現(xiàn)滑動(dòng)拼圖驗(yàn)證碼功能

    js+canvas實(shí)現(xiàn)滑動(dòng)拼圖驗(yàn)證碼功能

    這篇文章主要介紹了js+canvas實(shí)現(xiàn)滑動(dòng)拼圖驗(yàn)證碼功能,本文結(jié)合實(shí)例代碼分步驟給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2018-03-03
  • 你未必知道的JavaScript和CSS交互的5種方法

    你未必知道的JavaScript和CSS交互的5種方法

    網(wǎng)頁(yè)中都有.js文件和.css文件,但這并不意味著CSS和js是獨(dú)立不能交互的,下面與大家分享js與css交互的5種方法
    2014-04-04

最新評(píng)論