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

相关文章
相关标签/搜索