JS中的堆栈与拷贝(转载)
来源:互联网 发布:mysql 默认开启事件 编辑:程序博客网 时间:2024/05/21 09:23
JS堆栈与拷贝
一.堆栈的定义
1.栈是一种特殊的线性表。其特殊性在于限定插入和删除数据元素的操作只能在线性表的一端进行。
结论:后进先出(Last In First Out),简称为LIFO线性表。
栈的应用有:数制转换,语法词法分析,表达式求值等
2.队列(Queue)也是一种运算受限的线性表,它的运算限制与栈不同,是两头都有限制,插入只能在表的一端进行(只进不出),而删除只能在表的另一端进行(只出不进),允许删除的一端称为队尾(rear),允许插入的一端称为队头 (Front),队列的操作原则是先进先出的,所以队列又称作FIFO表(First In First Out)。
由于栈和队列也是线性表,栈和队列有顺序栈和链栈两种存储结构,这两种存储结构的不同,则使得实现栈的基本运算的算法也有所不同。
二.JS堆栈研究
1、栈(stack)和堆(heap)
stack为自动分配的内存空间,它由系统自动释放;而heap则是动态分配的内存,大小不定也不会自动释放。
2、基本类型和引用类型
(1) 基本类型 :存放在栈内存中的简单数据段,数据大小确定,内存空间大小可以分配。
5种基本数据类型有Undefined、Null、Boolean、Number 和 String,它们是直接按值存放的,所以可以直接访问。
(2) 引用类型 :存放在堆内存中的对象,变量实际保存的是一个指针,这个指针指向另一个位置。每个空间大小不一样,要根据情况开进行特定的分配。
当我们需要访问引用类型(如对象,数组,函数等)的值时,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据。
3、传值与传址
前面之所以要说明什么是内存中的堆、栈以及变量类型,实际上是为了更好的理解什么是“浅拷贝”和“深拷贝”。
基本类型与引用类型最大的区别实际就是传值与传址的区别。测试用例:
var a = [1,2,3,4,5];var b = a;var c = a[0];alert(b);//1,2,3,4,5 alert(c);//1 //改变数值 b[4] = 6;c = 7;alert(a[4]);//6alert(a[0]);//1
从上面我们可以得知,当我改变b中的数据时,a中数据也发生了变化;但是当我改变c的数据值时,a却没有发生改变。这就是传值与传址的区别。因为a是数组,属于引用类型,所以它赋予给b的时候传的是栈中的地址(相当于新建了一个不同名“指针”),而不是堆内存中的对象。而c仅仅是从a堆内存中获取的一个数据值,并保存在栈中。所以b修改的时候,会根据地址回到a堆中修改,c则直接在栈中修改,并且不能指向a堆内存中。
三.拷贝
1.浅拷贝
前面已经提到,在定义一个对象或数组时,变量存放的往往只是一个地址。当我们使用对象拷贝时,如果属性是对象或数组时,这时候我们传递的也只是一个地址。因此子对象在访问该属性时,会根据地址回溯到父对象指向的堆内存中,即父子对象发生了关联,两者的属性值会指向同一内存空间。
var a = {key1:"11111" }function Copy(p) {var c = {};for (var i in p) { c[i] = p[i]; }return c; } a.key2 = ['小辉','小辉'];var b = Copy(a); b.key3 = '33333'; alert(b.key1); //1111111 alert(b.key3); //33333 alert(a.key3); //undefined
a对象中key1属性是字符串,key2属性是数组。a拷贝到b,12属性均顺利拷贝。给b对象新增一个字符串类型的属性key3时,b能正常修改,而a中无定义。说明子对象的key3(基本类型)并没有关联到父对象中,所以undefined。
b.key2.push("大辉");alert(b.key2); //小辉,小辉,大辉alert(a.key2); //小辉,小辉,大辉
但是,若修改的属性变为对象或数组时,那么父子对象之间就会发生关联。从以上弹出结果可知,我对b对象进行修改,a、b的key2属性值(数组)均发生了改变。其在内存的状态,可以用下图来表示。
原因是key1的值属于基本类型,所以拷贝的时候传递的就是该数据段;但是key2的值是堆内存中的对象,所以key2在拷贝的时候传递的是指向key2对象的地址,无论复制多少个key2,其值始终是指向父对象的key2对象的内存空间。
2.深度拷贝(下面的函数有bug请看深度拷贝代码片1,我已经将其优化,可以查看深度拷贝代码片2,文章结尾)
或许以上并不是我们在实际编码中想要的结果,我们不希望父子对象之间产生关联,那么这时候可以用到深拷贝。既然属性值类型是数组和或象时只会传址,那么我们就用递归来解决这个问题,把父对象中所有属于对象的属性类型都遍历赋给子对象即可。测试代码如下:
function Copy(p, c) {var c = c || {};for (var i in p) {if (typeof p[i] === 'object') { c[i] = (p[i].constructor === Array) ? [] : {}; Copy(p[i], c[i]); } else { c[i] = p[i];} }return c; } a.key2 = ['小辉','小辉'];var b={};b = Copy(a,b); b.key2.push("大辉");alert(b.key2); //小辉,小辉,大辉alert(a.key2); //小辉,小辉
由上可知,修改b的key2数组时,没有使a父对象中的key2数组新增一个值,即子对象没有影响到父对象a中的key2。其存储模式大致如下:
深度拷贝代码片1:
// 深度拷贝数组的时候,如果我们的目标对象原本初始化为{},那么拷贝后的目标对象是一个object不是数组 var p = [{key1: 1,key2: 2},[1,2,3,4,4,5]];var c = {};c = Copy(p,c);console.log(p); // [Object, Array[6]]console.log(c); // Object {0: Object, 1: Array[6]}
深度拷贝代码片2:
function Copy(p, c) {var c = isArray(p) ? [] : {}; // 根据被拷贝对象的数据类型对目标对象进行类型初始化for (var i in p) {if (typeof p[i] === 'object') { c[i] = (isArray(p[i]) === Array) ? [] : {}; c[i] = Copy(p[i], c[i]); } else { c[i] = p[i];} }return c;function isArray(obj){return obj !== undefined && Object.prototype.toString.call(p) === '[object Array]';}} var p = [{key1: 1,key2: 2},[1,2,3,4,4,5]];var c = {};c = Copy(p,c);console.log(p); console.log(c);
原文链接:http://www.tuicool.com/articles/EviuUfV点击打开链接
- JS中的堆栈与拷贝(转载)
- JS堆栈与拷贝
- 关于JS堆栈与拷贝
- Js中的深拷贝与浅拷贝
- 关于JS堆栈与拷贝——小辉_Ray
- C语言中的位拷贝与值拷贝浅谈(转载)
- 说说JS中的浅拷贝与深拷贝
- JS堆栈和拷贝的理解
- 转载 C# 深拷贝与浅拷贝
- 数据结构的堆栈 与Java中的堆栈
- js中的浅拷贝和深拷贝
- js中的深拷贝和浅拷贝
- C++异常中的堆栈跟踪 [转载]
- js 深拷贝与浅拷贝Demo
- js 浅拷贝与深拷贝
- JS深拷贝与浅拷贝
- js对象浅拷贝与深拷贝
- JS的深拷贝与浅拷贝
- tcp/ip 19 经受时延的确认。。
- ubuntu 14.04 server 编译安装最新版git V2.11-rc2
- Git Bash提交代码避免每次输入用户密码
- python 计时
- iOS如何使用自己添加的字体库
- JS中的堆栈与拷贝(转载)
- java设计模式之外观设计模式
- vbox安装ukylin 16.10
- 按钮实现鼠标悬停背景色渐变的动画特效
- cocos-js html5网页版修改默认背景颜色
- 表单提交前的验证
- 【OpenCV学习笔记 016】图像分割-种子区域生长
- 【HDU5730】Shell Necklace——CDQ+FFT
- 【行研】工控安全之工控厂商防护思路