溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

ES6深拷貝與淺拷貝

發(fā)布時(shí)間:2020-08-06 12:44:11 來(lái)源:網(wǎng)絡(luò) 閱讀:61796 作者:googlingman 欄目:開(kāi)發(fā)技術(shù)

小感在前

React學(xué)習(xí)與開(kāi)發(fā)過(guò)程要經(jīng)歷一個(gè)相當(dāng)長(zhǎng)的準(zhǔn)備階段,此前看阮一峰老師的文章中,他就特別提到這一點(diǎn)。但是,由于React框架或者說(shuō)是一種優(yōu)秀的前端架構(gòu)實(shí)在太誘人,所以,掌握這項(xiàng)技術(shù)所涉及的技術(shù)棧過(guò)程中經(jīng)歷的任何苦惱都是值得的。在還沒(méi)有全面掌握這項(xiàng)技術(shù)的不斷探索過(guò)程中,如果包括后端的一部分在內(nèi)(例如Node.js和Express等,還有一些數(shù)據(jù)庫(kù)及API知識(shí)),至少要有幾十部分,僅限于核心的算起來(lái)也有十多項(xiàng)吧。
作為一個(gè)基礎(chǔ)部分,ES6勢(shì)在必學(xué)。此前,我曾得意于自己已經(jīng)掌握了一定程度的JS知識(shí),但是在匆讀阮老師的《ES6標(biāo)準(zhǔn)入門(mén)》一書(shū)后才感覺(jué)自己落后許多了。同時(shí),在閱讀本書(shū)的過(guò)程中,也發(fā)現(xiàn):這本書(shū)或者整個(gè)ES6,的確是為著眼于將來(lái)的“一統(tǒng)前后端”的大項(xiàng)目作準(zhǔn)備的,因此,重復(fù)地專(zhuān)門(mén)學(xué)習(xí)這個(gè)語(yǔ)言是沒(méi)有必要的。這是一本案頭書(shū),需要什么時(shí)再讀什么更合適。
本文羅列的就是在本人在學(xué)習(xí)Redux過(guò)程中在遭遇到JS的深淺拷貝過(guò)程中總結(jié)的相關(guān)內(nèi)容。太匆忙與粗略,僅供同學(xué)們參考。

JavaScript 中變量的賦值

結(jié)論:JavaScript中變量的賦值分為「?jìng)髦怠古c「?jìng)髦贰埂?

基本數(shù)據(jù)類(lèi)型的賦值,就是「?jìng)髦怠?;而引用?lèi)型變量賦值,實(shí)際上是「?jìng)髦贰埂?br/>基本數(shù)據(jù)類(lèi)型變量的賦值、比較,只是值的賦值和比較,也即棧內(nèi)存中的數(shù)據(jù)的拷貝和比較,參見(jiàn)如下代碼:

var num1 = 123;
var num2 = 123;
var num3 = num1;
num1 === num2; // true
num1 === num3; // true
num1 = 456;
num1 === num2; // false
num1 === num3; // false

引用數(shù)據(jù)類(lèi)型變量的賦值、比較,只是存于棧內(nèi)存中的堆內(nèi)存地址的拷貝、比較,參加如下代碼:

var arr1 = [1, 2, 3];
var arr2 = [1, 2, 3];
var arr3 = arr1;
arr1 === arr2; // false
arr1 === arr3; // true
arr1 = [1, 2, 3];
arr1 === arr2; // false
arr1 === arr3; // false

JavaScript 中變量的拷貝

JavaScript中的拷貝區(qū)分為「淺拷貝」與「深拷貝」。

淺拷貝

淺拷貝只會(huì)將對(duì)象的各個(gè)屬性進(jìn)行依次復(fù)制,并不會(huì)進(jìn)行遞歸復(fù)制,也就是說(shuō)只會(huì)進(jìn)行賦值目標(biāo)對(duì)象的第一層屬性。

對(duì)于目標(biāo)對(duì)象第一層為基本數(shù)據(jù)類(lèi)型的數(shù)據(jù),就是直接賦值,即「?jìng)髦怠?;而?duì)于目標(biāo)對(duì)象第一層為引用數(shù)據(jù)類(lèi)型的數(shù)據(jù),就是直接賦存于棧內(nèi)存中的堆內(nèi)存地址,即「?jìng)髦贰埂?/p>

深拷貝

深拷貝不同于淺拷貝,它不但拷貝目標(biāo)對(duì)象的第一層屬性,而且還遞歸拷貝目標(biāo)對(duì)象的所有屬性。

一般來(lái)說(shuō),在JavaScript中考慮復(fù)合類(lèi)型的深層復(fù)制的時(shí)候,往往就是指對(duì)于 Date 、Object 與 Array 這三個(gè)復(fù)合類(lèi)型的處理。我們能想到的最常用的方法就是先創(chuàng)建一個(gè)空的新對(duì)象,然后遞歸遍歷舊對(duì)象,直到發(fā)現(xiàn)基礎(chǔ)類(lèi)型的子節(jié)點(diǎn)才賦予到新對(duì)象對(duì)應(yīng)的位置。

不過(guò)這種方法會(huì)存在一個(gè)問(wèn)題,就是 JavaScript 中存在著神奇的原型機(jī)制,并且這個(gè)原型會(huì)在遍歷的時(shí)候出現(xiàn),然后需要考慮原型應(yīng)不應(yīng)該被賦予給新對(duì)象。那么在遍歷的過(guò)程中,我們可以考慮使用 hasOwnProperty 方法來(lái)判斷是否過(guò)濾掉那些繼承自原型鏈上的屬性。

Object.assign

Object.assign 方法可以把 任意多個(gè)的源對(duì)象所擁有的自身可枚舉屬性 拷貝給目標(biāo)對(duì)象,然后返回目標(biāo)對(duì)象。
注意:

對(duì)于訪(fǎng)問(wèn)器屬性,該方法會(huì)執(zhí)行那個(gè)訪(fǎng)問(wèn)器屬性的 getter 函數(shù),然后把得到的值拷貝給目標(biāo)對(duì)象,如果你想拷貝訪(fǎng)問(wèn)器屬性本身,請(qǐng)使用 Object.getOwnPropertyDescriptor() 和 Object.defineProperties() 方法;
字符串類(lèi)型和 symbol 類(lèi)型的屬性都會(huì)被拷貝;
在屬性拷貝過(guò)程中可能會(huì)產(chǎn)生異常,比如目標(biāo)對(duì)象的某個(gè)只讀屬性和源對(duì)象的某個(gè)屬性同名,這時(shí)該方法會(huì)拋出一個(gè) TypeError 異常,拷貝過(guò)程中斷,已經(jīng)拷貝成功的屬性不會(huì)受到影響,還未拷貝的屬性將不會(huì)再被拷貝;
該方法會(huì)跳過(guò)那些值為 null 或 undefined 的源對(duì)象;

利用 JSON 進(jìn)行忽略原型鏈的深拷貝

var dest = JSON.parse(JSON.stringify(target));

同樣的它也有缺點(diǎn):
該方法會(huì)忽略掉值為 undefined 的屬性以及函數(shù)表達(dá)式,但不會(huì)忽略值為 null 的屬性。

借助于Lodash 的 merge 方法

在Redux開(kāi)發(fā)中的一個(gè)應(yīng)用是借助于Lodash 的 merge 方法可以實(shí)現(xiàn)深拷貝,達(dá)到管理范式化數(shù)據(jù)的目的,示例代碼如下:

import merge from "lodash/object/merge";

function commentsById(state = {}, action) {
    switch(action.type) {
        default : {
           if(action.entities && action.entities.comments) {
               return merge({}, state, action.entities.comments.byId);
           }
           return state;
        }
    }
}

存在大量深拷貝需求時(shí)借助immutable庫(kù)

實(shí)際上,即使我們知道了如何在各種情況下進(jìn)行深拷貝,我們也仍然面臨一些問(wèn)題: 深拷貝實(shí)際上是很消耗性能的。(我們可能只是希望改變新數(shù)組里的其中一個(gè)元素的時(shí)候不影響原數(shù)組,但卻被迫要把整個(gè)原數(shù)組都拷貝一遍,這不是一種浪費(fèi)嗎?)所以,當(dāng)你的項(xiàng)目里有大量深拷貝需求的時(shí)候,性能就可能形成了一個(gè)制約的瓶頸了。

immutable的作用是通過(guò)immutable引入的一套API,實(shí)現(xiàn):

1.在改變新的數(shù)組(對(duì)象)的時(shí)候,不改變?cè)瓟?shù)組(對(duì)象)
2.在大量深拷貝操作中顯著地減少性能消耗

參考代碼:
const { Map } = require('immutable')
const map1 = Map({ a: 1, b: 2, c: 3 })
const map2 = map1.set('b', 50)
map1.get('b') // 2
map2.get('b') // 50

盡量保持?jǐn)?shù)據(jù)設(shè)計(jì)的扁平化與科學(xué)設(shè)計(jì)Reducer

在React+Redux開(kāi)發(fā)中,經(jīng)常要遇到不可變數(shù)據(jù)的更新問(wèn)題。復(fù)雜情形中,往往需要復(fù)制嵌套數(shù)據(jù)的所有層級(jí)。但不幸的是,正確地使用不變的更新去深度嵌套狀態(tài)的過(guò)程很容易變得冗長(zhǎng)難讀。 更新 ate.first.second[someId].fourth 的示例(http://www.redux.org.cn/docs/recipes/reducers/ImmutableUpdatePatterns.html)大概如下所示:

function updateVeryNestedField(state, action) {
    return {
        ....state,
        first : {
            ...state.first,
            second : {
                ...state.first.second,
                [action.someId] : {
                    ...state.first.second[action.someId],
                    fourth : action.someValue
                }
            }
        }
    }
}

顯然,每一層嵌套使得閱讀更加困難,并給了更多犯錯(cuò)的機(jī)會(huì)。這是其中一個(gè)原因,鼓勵(lì)你保持狀態(tài)扁平,盡可能構(gòu)建小巧而靈活的Reducer。

引用

1,https://juejin.im/post/5acc7e606fb9a028c67609f7
2,https://zhuanlan.zhihu.com/p/28508795
3,http://www.zsoltnagy.eu/cloning-objects-in-javascript/
4,http://www.cnblogs.com/penghuwan/p/7359026.html
5,https://blog.csdn.net/github_38524608/article/details/78812761
6,https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Spread_syntax
7,http://www.redux.org.cn/docs/recipes/reducers/UpdatingNormalizedData.html

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI