ES6深拷貝與淺拷貝

小感在前

React學習與開發過程要經歷一個至關長的準備階段,此前看阮一峯老師的文章中,他就特別提到這一點。可是,因爲React框架或者說是一種優秀的前端架構實在太誘人,因此,掌握這項技術所涉及的技術棧過程當中經歷的任何苦惱都是值得的。在尚未全面掌握這項技術的不斷探索過程當中,若是包括後端的一部分在內(例如Node.js和Express等,還有一些數據庫及API知識),至少要有幾十部分,僅限於核心的算起來也有十多項吧。
做爲一個基礎部分,ES6勢在必學。此前,我曾得意於本身已經掌握了必定程度的JS知識,可是在匆讀阮老師的《ES6標準入門》一書後才感受本身落後許多了。同時,在閱讀本書的過程當中,也發現:這本書或者整個ES6,的確是爲着眼於未來的「一統先後端」的大項目做準備的,所以,重複地專門學習這個語言是沒有必要的。這是一本案頭書,須要什麼時再讀什麼更合適。
本文羅列的就是在本人在學習Redux過程當中在遭遇到JS的深淺拷貝過程當中總結的相關內容。太匆忙與粗略,僅供同窗們參考。javascript

JavaScript 中變量的賦值

結論:JavaScript中變量的賦值分爲「傳值」與「傳址」。

基本數據類型的賦值,就是「傳值」;而引用類型變量賦值,其實是「傳址」。
基本數據類型變量的賦值、比較,只是值的賦值和比較,也即棧內存中的數據的拷貝和比較,參見以下代碼:html

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

引用數據類型變量的賦值、比較,只是存於棧內存中的堆內存地址的拷貝、比較,參加以下代碼:前端

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中的拷貝區分爲「淺拷貝」與「深拷貝」。java

淺拷貝

淺拷貝只會將對象的各個屬性進行依次複製,並不會進行遞歸複製,也就是說只會進行賦值目標對象的第一層屬性。git

對於目標對象第一層爲基本數據類型的數據,就是直接賦值,即「傳值」;而對於目標對象第一層爲引用數據類型的數據,就是直接賦存於棧內存中的堆內存地址,即「傳址」。github

深拷貝

深拷貝不一樣於淺拷貝,它不但拷貝目標對象的第一層屬性,並且還遞歸拷貝目標對象的全部屬性。數據庫

通常來講,在JavaScript中考慮複合類型的深層複製的時候,每每就是指對於 Date 、Object 與 Array 這三個複合類型的處理。咱們能想到的最經常使用的方法就是先建立一個空的新對象,而後遞歸遍歷舊對象,直到發現基礎類型的子節點才賦予到新對象對應的位置。redux

不過這種方法會存在一個問題,就是 JavaScript 中存在着神奇的原型機制,而且這個原型會在遍歷的時候出現,而後須要考慮原型應不該該被賦予給新對象。那麼在遍歷的過程當中,咱們能夠考慮使用 hasOwnProperty 方法來判斷是否過濾掉那些繼承自原型鏈上的屬性。後端

Object.assign

Object.assign 方法能夠把 任意多個的源對象所擁有的自身可枚舉屬性 拷貝給目標對象,而後返回目標對象。
注意:數組

對於訪問器屬性,該方法會執行那個訪問器屬性的 getter 函數,而後把獲得的值拷貝給目標對象,若是你想拷貝訪問器屬性自己,請使用 Object.getOwnPropertyDescriptor() 和 Object.defineProperties() 方法;
字符串類型和 symbol 類型的屬性都會被拷貝;
在屬性拷貝過程當中可能會產生異常,好比目標對象的某個只讀屬性和源對象的某個屬性同名,這時該方法會拋出一個 TypeError 異常,拷貝過程當中斷,已經拷貝成功的屬性不會受到影響,還未拷貝的屬性將不會再被拷貝;
該方法會跳過那些值爲 null 或 undefined 的源對象;

利用 JSON 進行忽略原型鏈的深拷貝

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

一樣的它也有缺點:
該方法會忽略掉值爲 undefined 的屬性以及函數表達式,但不會忽略值爲 null 的屬性。

藉助於Lodash 的 merge 方法

在Redux開發中的一個應用是藉助於Lodash 的 merge 方法能夠實現深拷貝,達到管理範式化數據的目的,示例代碼以下:

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

存在大量深拷貝需求時藉助immutable庫

實際上,即便咱們知道了如何在各類狀況下進行深拷貝,咱們也仍然面臨一些問題: 深拷貝其實是很消耗性能的。(咱們可能只是但願改變新數組裏的其中一個元素的時候不影響原數組,但卻被迫要把整個原數組都拷貝一遍,這不是一種浪費嗎?)因此,當你的項目裏有大量深拷貝需求的時候,性能就可能造成了一個制約的瓶頸了。

immutable的做用是經過immutable引入的一套API,實現:

1.在改變新的數組(對象)的時候,不改變原數組(對象)
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

儘可能保持數據設計的扁平化與科學設計Reducer

在React+Redux開發中,常常要遇到不可變數據的更新問題。複雜情形中,每每須要複製嵌套數據的全部層級。但不幸的是,正確地使用不變的更新去深度嵌套狀態的過程很容易變得冗長難讀。 更新 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
                }
            }
        }
    }
}

顯然,每一層嵌套使得閱讀更加困難,並給了更多犯錯的機會。這是其中一個緣由,鼓勵你保持狀態扁平,儘量構建小巧而靈活的Reducer。

引用

1,http://www.javashuo.com/article/p-snxlfqsh-hx.html
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

相關文章
相關標籤/搜索