成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

?搞不懂JS中賦值·淺拷貝·深拷貝的請(qǐng)看這里

lauren_liuling / 491人閱讀

前言

為什么寫拷貝這篇文章?同事有一天提到了拷貝,他說(shuō)賦值就是一種淺拷貝方式,另一個(gè)同事說(shuō)賦值和淺拷貝并不相同。
我也有些疑惑,于是我去MDN搜一下拷貝相關(guān)內(nèi)容,發(fā)現(xiàn)并沒(méi)有關(guān)于拷貝的實(shí)質(zhì)概念,沒(méi)有辦法只能通過(guò)實(shí)踐了,同時(shí)去看一些前輩們的文章總結(jié)了這篇關(guān)于拷貝的內(nèi)容,本文也屬于公眾號(hào)【程序員成長(zhǎng)指北】學(xué)習(xí)路線中【JS必知必會(huì)】?jī)?nèi)容。

數(shù)據(jù)類型與堆棧的關(guān)系 基本類型與引用類型

基本類型:undefined,null,Boolean,String,Number,Symbol

引用類型:Object,Array,Date,Function,RegExp等

存儲(chǔ)方式

基本類型:基本類型值在內(nèi)存中占據(jù)固定大小,保存在棧內(nèi)存中(不包含閉包中的變量)

引用類型:引用類型的值是對(duì)象,保存在堆內(nèi)存中。而棧內(nèi)存存儲(chǔ)的是對(duì)象的變量標(biāo)識(shí)符以及對(duì)象在堆內(nèi)存中的存儲(chǔ)地址(引用),引用數(shù)據(jù)類型在棧中存儲(chǔ)了指針,該指針指向堆中該實(shí)體的起始地址。當(dāng)解釋器尋找引用值時(shí),會(huì)首先檢索其在棧中的地址,取得地址后從堆中獲得實(shí)體。

注意:

閉包中的變量并不保存在棧內(nèi)存中,而是保存在堆內(nèi)存中。這一點(diǎn)比較好想,如果閉包中的變量保存在了棧內(nèi)存中,隨著外層中的函數(shù)從調(diào)用棧中銷毀,變量肯定也會(huì)被銷毀,但是如果保存在了堆內(nèi)存中,內(nèi)存函數(shù)仍能訪問(wèn)外層已銷毀函數(shù)中的變量??匆欢螌?duì)應(yīng)代碼理解下:

function A() {
  let a = "koala"
  function B() {
      console.log(a)
  }
  return B
}

本篇所講的淺拷貝和深拷貝都是對(duì)于引用類型的,對(duì)于基礎(chǔ)類型不會(huì)有這種操作。

賦值操作 基本數(shù)據(jù)類型復(fù)制

看一段代碼

let a ="koala";
let b = a;
b="程序員成長(zhǎng)指北";
console.log(a); // koala

基本數(shù)據(jù)類型復(fù)制配圖:

結(jié)論:在棧內(nèi)存中的數(shù)據(jù)發(fā)生數(shù)據(jù)變化的時(shí)候,系統(tǒng)會(huì)自動(dòng)為新的變量分配一個(gè)新的之值在棧內(nèi)存中,兩個(gè)變量相互獨(dú)立,互不影響的。

引用數(shù)據(jù)類型復(fù)制

看一段代碼

let a = {x:"kaola", y:"kaola1"}
let b = a;
b.x = "程序員成長(zhǎng)指北";
console.log(a.x); // 程序員成長(zhǎng)指北

引用數(shù)據(jù)類型復(fù)制配圖:


結(jié)論:引用類型的復(fù)制,同樣為新的變量b分配一個(gè)新的值,報(bào)錯(cuò)在棧內(nèi)存中,不同的是這個(gè)變量對(duì)應(yīng)的具體值不在棧中,棧中只是一個(gè)地址指針。兩個(gè)變量地址指針相同,指向堆內(nèi)存中的對(duì)象,因此b.x發(fā)生改變的時(shí)候,a.x也發(fā)生了改變。

淺拷貝 淺拷貝定義:

不知道的api我一般比較喜歡看MDN,淺拷貝的概念MDN官方并沒(méi)有給出明確定義,但是搜到了一個(gè)函數(shù)Array.prototype.slice,官方說(shuō)它可以實(shí)現(xiàn)原數(shù)組的淺拷貝。
對(duì)于官方給的結(jié)論,我們通過(guò)兩段代碼驗(yàn)證一下,并總結(jié)出淺拷貝的定義。

第一段代碼:

var a = [ 1, 3, 5, { x: 1 } ];
var b = Array.prototype.slice.call(a);
b[0] = 2;
console.log(a); // [ 1, 3, 5, { x: 1 } ];
console.log(b); // [ 2, 3, 5, { x: 1 } ];

從輸出結(jié)果可以看出,淺拷貝后,數(shù)組a[0]并不會(huì)隨著b[0]改變而改變,說(shuō)明a和b在棧內(nèi)存中引用地址并不相同。

第二段代碼

var a = [ 1, 3, 5, { x: 1 } ];
var b = Array.prototype.slice.call(a);
b[3].x = 2;
console.log(a); // [ 1, 3, 5, { x: 2 } ];
console.log(b); // [ 1, 3, 5, { x: 2 } ];

從輸出結(jié)果可以看出,淺拷貝后,數(shù)組中對(duì)象的屬性會(huì)根據(jù)修改而改變,說(shuō)明淺拷貝的時(shí)候拷貝的已存在對(duì)象的對(duì)象的屬性引用。

淺拷貝定義

通過(guò)這個(gè)官方的slice淺拷貝函數(shù)分析淺拷貝定義

新的對(duì)象復(fù)制已有對(duì)象中非對(duì)象屬性的值和對(duì)象屬性的引用。如果這種說(shuō)法不理解換一種一個(gè)新的對(duì)象直接拷貝已存在的對(duì)象的對(duì)象屬性的引用,即淺拷貝。
淺拷貝實(shí)例 Object.assign

語(yǔ)法:

語(yǔ)法:Object.assign(target, ...sources)

ES6中拷貝對(duì)象的方法,接受的第一個(gè)參數(shù)是拷貝的目標(biāo)target,剩下的參數(shù)是拷貝的源對(duì)象sources(可以是多個(gè))

舉例說(shuō)明:

let target = {};
let source = {a:"koala",b:{name:"程序員成長(zhǎng)指北"}};
Object.assign(target ,source);
console.log(target); // { a: "koala", b: { name: "程序員成長(zhǎng)指北" } }
source.a = "smallKoala";
source.b.name = "程序員成長(zhǎng)指北哦"
console.log(source); // { a: "smallKoala", b: { name: "程序員成長(zhǎng)指北哦" } }
console.log(target); // { a: "koala", b: { name: "程序員成長(zhǎng)指北哦" } }

從打印結(jié)果可以看出,Object.assign是一個(gè)淺拷貝,它只是在根屬性(對(duì)象的第一層級(jí))創(chuàng)建了一個(gè)新的對(duì)象,但是對(duì)于屬性的值是對(duì)象的話只會(huì)拷貝一份相同的內(nèi)存地址。

Object.assign注意事項(xiàng)

只拷貝源對(duì)象的自身屬性(不拷貝繼承屬性)

它不會(huì)拷貝對(duì)象不可枚舉的屬性

undefinednull無(wú)法轉(zhuǎn)成對(duì)象,它們不能作為Object.assign參數(shù),但是可以作為源對(duì)象

Object.assign(undefined) // 報(bào)錯(cuò)
Object.assign(null) // 報(bào)錯(cuò)

let obj = {a: 1};
Object.assign(obj, undefined) === obj // true
Object.assign(obj, null) === obj // true

屬性名為 Symbol 值的屬性,可以被Object.assign拷貝。

Array.prototype.slice

這個(gè)函數(shù)在淺拷貝概念定義的時(shí)候已經(jīng)進(jìn)行了分析,看上文。

Array.prototype.concat

語(yǔ)法

var new_array = old_array.concat(value1[, value2[, ...[, valueN]]])
參數(shù):將數(shù)組和/或值連接成新數(shù)組

舉例說(shuō)明

let array = [{a: 1}, {b: 2}];
let array1 = [{c: 3},{d: 4}];
let array2=array.concat(array1);
array1[0].c=123;
console.log(array2);// [ { a: 1 }, { b: 2 }, { c: 123 }, { d: 4 } ]
console.log(array1);// [ { c: 123 }, { d: 4 } ]

Array.prototype.concat也是一個(gè)淺拷貝,只是在根屬性(對(duì)象的第一層級(jí))創(chuàng)建了一個(gè)新的對(duì)象,但是對(duì)于屬性的值是對(duì)象的話只會(huì)拷貝一份相同的內(nèi)存地址。

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

語(yǔ)法

var cloneObj = { ...obj };

舉例說(shuō)明

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}}

擴(kuò)展運(yùn)算符也是淺拷貝,對(duì)于值是對(duì)象的屬性無(wú)法完全拷貝成2個(gè)不同對(duì)象,但是如果屬性都是基本類型的值的話,使用擴(kuò)展運(yùn)算符也是優(yōu)勢(shì)方便的地方。

補(bǔ)充說(shuō)明:以上4中淺拷貝方式都不會(huì)改變?cè)瓟?shù)組,只會(huì)返回一個(gè)淺拷貝了原數(shù)組中的元素的一個(gè)新數(shù)組。

自己實(shí)現(xiàn)一個(gè)淺拷貝

實(shí)現(xiàn)原理:新的對(duì)象復(fù)制已有對(duì)象中非對(duì)象屬性的值和對(duì)象屬性的引用,也就是說(shuō)對(duì)象屬性并不復(fù)制到內(nèi)存。

實(shí)現(xiàn)代碼:

function cloneShallow(source) {
    var target = {};
    for (var key in source) {
        if (Object.prototype.hasOwnProperty.call(source, key)) {
            target[key] = source[key];
        }
    }
    return target;
}

for in與hasOwnProperty函數(shù)說(shuō)明,怕有些人小伙伴可能不清楚具體內(nèi)容

for in

for...in語(yǔ)句以任意順序遍歷一個(gè)對(duì)象自有的、繼承的、可枚舉的、非Symbol的屬性。對(duì)于每個(gè)不同的屬性,語(yǔ)句都會(huì)被執(zhí)行。

hasOwnProperty

語(yǔ)法:obj.hasOwnProperty(prop)      
prop是要檢測(cè)的屬性字符串名稱或者Symbol

該函數(shù)返回值為布爾值,所有繼承了 Object 的對(duì)象都會(huì)繼承到 hasOwnProperty 方法,和 in 運(yùn)算符不同,該函數(shù)會(huì)忽略掉那些從原型鏈上繼承到的屬性和自身屬性。

深拷貝操作

說(shuō)了賦值操作和淺拷貝操作,大家是不是已經(jīng)能想到什么是深拷貝了,下面直接說(shuō)深拷貝的定義。

深拷貝定義
深拷貝會(huì)另外拷貝一份一個(gè)一模一樣的對(duì)象,從堆內(nèi)存中開辟一個(gè)新的區(qū)域存放新對(duì)象,新對(duì)象跟原對(duì)象不共享內(nèi)存,修改新對(duì)象不會(huì)改到原對(duì)象。
深拷貝實(shí)例 JSON.parse(JSON.stringify())

JSON.stringify()是前端開發(fā)過(guò)程中比較常用的深拷貝方式。原理是把一個(gè)對(duì)象序列化成為一個(gè)JSON字符串,將對(duì)象的內(nèi)容轉(zhuǎn)換成字符串的形式再保存在磁盤上,再用JSON.parse()反序列化將JSON字符串變成一個(gè)新的對(duì)象

舉例說(shuō)明:

let arr = [1, 3, {
    username: " koala"
}];
let arr4 = JSON.parse(JSON.stringify(arr));
arr4[2].username = "smallKoala"; 
console.log(arr4);// [ 1, 3, { username: "smallKoala" } ]
console.log(arr);// [ 1, 3, { username: " koala" } ]

實(shí)現(xiàn)了深拷貝,當(dāng)改變數(shù)組中對(duì)象的值時(shí)候,原數(shù)組中的內(nèi)容并沒(méi)有發(fā)生改變。JSON.stringify()雖然可以實(shí)現(xiàn)深拷貝,但是還有一些弊端比如不能處理函數(shù)等。

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)

自己實(shí)現(xiàn)一個(gè)簡(jiǎn)單深拷貝

深拷貝,主要用到的思想是遞歸,遍歷對(duì)象、數(shù)組直到里邊都是基本數(shù)據(jù)類型,然后再去復(fù)制,就是深度拷貝。
實(shí)現(xiàn)代碼:

    //定義檢測(cè)數(shù)據(jù)類型的功能函數(shù)
    function isObject(obj) {
        return typeof obj === "object" && obj != null;
    }
   function cloneDeep(source) {

    if (!isObject(source)) return source; // 非對(duì)象返回自身
      
    var target = Array.isArray(source) ? [] : {};
    for(var key in source) {
        if (Object.prototype.hasOwnProperty.call(source, key)) {
            if (isObject(source[key])) {
                target[key] = cloneDeep(source[key]); // 注意這里
            } else {
                target[key] = source[key];
            }
        }
    }
    return target;
}

該簡(jiǎn)單深拷貝未考慮內(nèi)容:
遇到循環(huán)引用,會(huì)陷入一個(gè)循環(huán)的遞歸過(guò)程,從而導(dǎo)致爆棧

// RangeError: Maximum call stack size exceeded

小伙伴們有沒(méi)有什么好辦法呢,可以寫下代碼在評(píng)論區(qū)一起討論哦!

第三方深拷貝庫(kù)

該函數(shù)庫(kù)也有提供_.cloneDeep用來(lái)做 Deep Copy(lodash是一個(gè)不錯(cuò)的第三方開源庫(kù),有好多不錯(cuò)的函數(shù),也可以看具體的實(shí)現(xiàn)源碼)

var _ = require("lodash");
var obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);
// false
拷貝內(nèi)容總結(jié)

用一張圖總結(jié)

今天就分享這么多,如果對(duì)分享的內(nèi)容感興趣,可以關(guān)注公眾號(hào)「程序員成長(zhǎng)指北」,或者加入技術(shù)交流群,大家一起討論。

進(jìn)階技術(shù)路線


加入我們一起學(xué)習(xí)吧!

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/105920.html

相關(guān)文章

  • JavaScript拷貝拷貝

    摘要:所以,深拷貝是對(duì)對(duì)象以及對(duì)象的所有子對(duì)象進(jìn)行拷貝實(shí)現(xiàn)方式就是遞歸調(diào)用淺拷貝對(duì)于深拷貝的對(duì)象,改變?cè)磳?duì)象不會(huì)對(duì)得到的對(duì)象有影響。 上一篇 JavaScript中的繼承 前言 文章開始之前,讓我們先思考一下這幾個(gè)問(wèn)題: 為什么會(huì)有淺拷貝與深拷貝 什么是淺拷貝與深拷貝 如何實(shí)現(xiàn)淺拷貝與深拷貝 好了,問(wèn)題出來(lái)了,那么下面就讓我們帶著這幾個(gè)問(wèn)題去探究一下吧! 如果文章中有出現(xiàn)紕漏、錯(cuò)誤之處...

    AZmake 評(píng)論0 收藏0
  • JavaScript拷貝拷貝

    摘要:所以,深拷貝是對(duì)對(duì)象以及對(duì)象的所有子對(duì)象進(jìn)行拷貝實(shí)現(xiàn)方式就是遞歸調(diào)用淺拷貝對(duì)于深拷貝的對(duì)象,改變?cè)磳?duì)象不會(huì)對(duì)得到的對(duì)象有影響。 為什么會(huì)有淺拷貝與深拷貝什么是淺拷貝與深拷貝如何實(shí)現(xiàn)淺拷貝與深拷貝好了,問(wèn)題出來(lái)了,那么下面就讓我們帶著這幾個(gè)問(wèn)題去探究一下吧! 如果文章中有出現(xiàn)紕漏、錯(cuò)誤之處,還請(qǐng)看到的小伙伴多多指教,先行謝過(guò) 以下↓ 數(shù)據(jù)類型在開始了解 淺拷貝 與 深拷貝 之前,讓我們先...

    546669204 評(píng)論0 收藏0
  • 拷貝拷貝

    摘要:二淺拷貝與深拷貝深拷貝和淺拷貝是只針對(duì)和這樣的引用數(shù)據(jù)類型的。淺拷貝是按位拷貝對(duì)象,它會(huì)創(chuàng)建一個(gè)新對(duì)象,這個(gè)對(duì)象有著原始對(duì)象屬性值的一份精確拷貝。對(duì)于字符串?dāng)?shù)字及布爾值來(lái)說(shuō)不是或者對(duì)象,會(huì)拷貝這些值到新的數(shù)組里。 一、數(shù)據(jù)類型 數(shù)據(jù)分為基本數(shù)據(jù)類型(String, Number, Boolean, Null, Undefined,Symbol)和對(duì)象數(shù)據(jù)類型。 基本數(shù)據(jù)類型的特點(diǎn):直...

    hzc 評(píng)論0 收藏0
  • 拷貝拷貝

    摘要:二淺拷貝與深拷貝深拷貝和淺拷貝是只針對(duì)和這樣的引用數(shù)據(jù)類型的。淺拷貝是按位拷貝對(duì)象,它會(huì)創(chuàng)建一個(gè)新對(duì)象,這個(gè)對(duì)象有著原始對(duì)象屬性值的一份精確拷貝。對(duì)于字符串?dāng)?shù)字及布爾值來(lái)說(shuō)不是或者對(duì)象,會(huì)拷貝這些值到新的數(shù)組里。 一、數(shù)據(jù)類型 數(shù)據(jù)分為基本數(shù)據(jù)類型(String, Number, Boolean, Null, Undefined,Symbol)和對(duì)象數(shù)據(jù)類型。 基本數(shù)據(jù)類型的特點(diǎn):直...

    史占廣 評(píng)論0 收藏0
  • L - 拷貝拷貝實(shí)現(xiàn)

    摘要:也就是說(shuō),深拷貝與淺拷貝最主要的區(qū)別在引用類型的拷貝上。方法二遞歸拷貝深拷貝與淺拷貝相比不就是多拷貝幾層的事嘛,這不就是遞歸常干的事嘛。 什么是淺拷貝和深拷貝 淺拷貝 淺拷貝:將一個(gè)對(duì)象自身的屬性拷貝給另一個(gè)對(duì)象,如果源對(duì)象的屬性是基本類型則直接進(jìn)行值賦值,如果是引用類型則進(jìn)行引用賦值,也就是說(shuō)只進(jìn)行一層賦值。 深拷貝 深拷貝:將一個(gè)對(duì)象自身的屬性拷貝給另一個(gè)對(duì)象,如果源對(duì)象的屬性是基...

    Anshiii 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<