js堆栈和拷贝

合集下载

JS数据结构及算法(一)堆栈

JS数据结构及算法(一)堆栈

JS数据结构及算法(⼀)堆栈最近在看《学习JavaScript数据结构与算法》这本书,感觉⾃⼰⼜涨知识了哈哈... 现在将⾃⼰看的做个总结,也是巩固理解。

栈:先进后出,新添加和待删除的元素都保存在栈顶。

可以⽤数组的push⽅法⼊栈,pop出栈。

class Stack {constructor () {this.items = [];}push(element){this.items.push(element);}pop(){return this.items.pop();}peek(){return this.items[this.items.length-1];}isEmpty(){return this.items.length == 0;}size(){return this.items.length;}clear(){this.items = [];}print(){console.log(this.toString());}toString(){return this.items.toString();}}栈的实际应⽤:⼆进制转⼗进制、⼗进制转换任意进制(⼆进制、⼋进制、⼗六进制);平衡圆括号、汉诺塔问题/*** ⼗进制转⼆进制* @param num --⼗进制数据* @returns {string} 转换后的⼆进制数*/function devideBy2(num) {let stack = new Stack();let rem;let binaryStr='';while(num>0){rem = num%2;stack.push(rem);num = Math.floor(num/2);}while (!stack.isEmpty()){binaryStr +=stack.pop().toString();}return binaryStr;}/*** ⼗进制转换为任意进制* @param num* @param base* @returns {string}*/function baseConvert(num,base) {let stack = new Stack();let rem;let baseStr='';let digit='0123456789ABCDEF'; //⼗六进制会转换while(num>0){rem = num%base;stack.push(rem);num = Math.floor(num/base);}while (!stack.isEmpty()){baseStr +=digit[stack.pop()];}return baseStr;}检查括号是否匹配:左括号⼊栈,当检测到右括号时,进⾏出栈,看出栈的左括号与右括号是否可以配对,以此类推,直到栈为空。

JS基本数据类型和引用数据类型的区别及深浅拷贝

JS基本数据类型和引用数据类型的区别及深浅拷贝

JS基本数据类型和引⽤数据类型的区别及深浅拷贝前⾔⾸先我们先来了解⼀下什么叫栈堆,基本数据类型与引⽤数据类型1.栈(stack)和堆(heap)stack为⾃动分配的内存空间,它由系统⾃动释放;⽽heap则是动态分配的内存,⼤⼩也不⼀定会⾃动释放。

2.基本的数据类型:String, Number, boolean, Null, Undefined,Symbol(ES6新增) 特点:存储的是该对象的实际数据,(存放在栈中)3.对象数据类型(也称为引⽤数据类型):Array,Object,Function 特点:存储的是该对象在栈中引⽤,真实的数据存放在堆内存⾥,(存放在堆内存中的对象,每个空间⼤⼩不⼀样,要根据情况进⾏特定的配置)注:在JS中除了基本数据类型以外的都是对象,数据是对象,函数是对象,正则表达式是对象1、区别:浅拷贝/深度拷贝判断:拷贝是否产⽣了新的数据还是拷贝的是数据的引⽤知识点:对象数据存放的是对象在栈内存的引⽤,直接复制的是对象的引⽤2、常⽤的拷贝技术 1). arr.concat(): 数组浅拷贝 2). arr.slice(): 数组浅拷贝 3).Object.assign()对象浅拷贝 4). JSON.parse(JSON.stringify(arr/obj)): 数组或对象深拷贝, 但不能处理函数数据 5). 浅拷贝包含函数数据的对象/数组 6). 深拷贝包含函数数据的对象/数组1.浅拷贝在ES6中,Object对象新增了⼀个assign⽅法,可以实现对象的浅复制。

这⾥谈谈Object.assign⽅法的具体⽤法,因为稍后会分析jQuery的extend⽅法,实现的原理同Object.assign⽅法差不多。

对于基本数据类型来说,复制⼀个变量值,本质上就是copy了这个变量。

⼀个变量值的修改,不会影响到另外⼀个变量。

<script type="text/javascript">/*1.直接赋值给⼀个变量*/let obj = {username:'genius'};console.log(obj); //genius//拷贝数组/对象没有⽣成新的数据⽽是复制了⼀份引⽤。

js深拷贝与浅拷贝的区别及实现

js深拷贝与浅拷贝的区别及实现

js深拷贝与浅拷贝的区别及实现参考: 要了解其本质区别,就需要了解堆和栈,值与引⽤的概念及区别1.堆(heap)和栈(stack)heap为⾃动分配的内存空间,它由系统⾃动释放;⽽stack则是动态分配的内存,⼤⼩不定也不会⾃动释放。

2.值与引⽤js中有基本数据类型和引⽤类型;基本数据类型的变量和值都是存放在栈中,声明之后会分配⼀块内存区域,基本数据类型之间的赋值是直接把栈内存中存的值赋值给变量(传值)引⽤类型的变量存在栈中,但值是存在堆中,实际上栈存放的是指向堆中的地址,也叫引⽤,引⽤类型直接的赋值实质是把引⽤赋值给⼀个变量(传址),所以其指向的堆内存中的值是⼀样的3.深拷贝和浅拷贝深拷贝和浅拷贝的使⽤场景是在复杂对象⾥,即对象的属性还是对象,浅拷贝是指只复制⼀层对象,当对象的属性是引⽤类型时,实质复制的是其引⽤,当引⽤指向的值改变时也会跟着变化例如:var obj = { a:1, arr: [2,3] };var shallowObj = shallowCopy(obj);function shallowCopy(src) {var dst = {};for (var prop in src) {if (src.hasOwnProperty(prop)) {dst[prop] = src[prop];}}return dst;}//当⼀个对象属性的引⽤值改变时将导致另⼀个也改变shallowObj.arr[1] = 5;obj.arr[1] // = 5深拷贝是指复制对象的所有层级,实现⽅法(1)通过递归实现deepCopy(o) {if (o instanceof Array) {let n = [];for (let i = 0; i < o.length; ++i) {n[i] =deepCopy(o[i]);}return n;} else if (o instanceof Object) {let n = {}for (let i in o) {n[i] = deepCopy(o[i]);}return n;} else {return o;}}(2)通过JSON解析实现//把⼀个对象转成json字符串在转成json对象JSON.parse(JSON.stringify(o))。

js复制数组的方法

js复制数组的方法

js复制数组的方法JavaScript是一种非常流行的编程语言,可用于Web开发、游戏开发和其他应用程序开发的许多不同领域。

在JavaScript编程中,我们通常需要使用数组,以便在程序中存储和操作数据。

数组是一组有序的元素,它们可以是任何类型的对象,比如数字、字符串、布尔值和其他数组等。

在JavaScript中,数组是非常灵活的,它允许我们添加、删除、修改和访问数组中的元素。

在某些情况下,我们需要复制一个数组,以便在程序中进行一些特定的操作。

本文将介绍一些可用于复制JavaScript数组的方法,包括浅拷贝和深拷贝。

一、浅拷贝浅拷贝是将原始数组的元素复制到一个新数组中,但是这些元素实际上仍然是原始数组中的对象的引用。

这意味着,如果我们对原始数组中的对象进行更改,则新的数组中的对应元素也将被更改。

对于像字符串和数字这样的简单类型,浅拷贝是非常有用的,但对于复杂类型的对象,我们需要使用深拷贝。

以下是几种进行浅拷贝的方法:1. 使用原始slice()方法以下是使用slice()方法复制数组的示例:```javascriptlet originalArray = [1, 2, 3, 4, 5];let newArray = originalArray.slice();```在这个例子中,我们使用slice()方法复制了originalArray数组,并将结果存储在newArray数组中。

由于我们没有提供slice()方法的参数,因此这将复制整个数组。

2. 使用ES6展开运算符展开运算符( ... )是ES6中新添加的运算符,它可以将数组展开成一个逗号分隔的元素列表。

展开运算符也可以用于复制一个数组,如下所示:```javascriptlet originalArray = [1, 2, 3, 4, 5];let newArray = [...originalArray];```在这个例子中,我们使用展开运算符将originalArray数组展开成一个元素列表,并将其复制到newArray数组中。

js中实现Stack栈类

js中实现Stack栈类

js中实现Stack栈类
栈(stack)⼜名堆栈,是⼀种类似列表的数据结构,栈内的元素只能从列表的⼀端进⾏访问,这⼀端成为栈顶,另⼀端称为栈底;栈遵循先进后出的原则,只允许在栈顶进⾏操作。

将元素添加进栈中被成为⼊栈(压栈)的⽅法push
将当前栈顶元素删除称为出栈的⽅法 pop
查看当前栈顶元素的⽅法 peek
查看当前栈的长度⽅法 size
删除栈的⽅法 clear
栈中的属性是top⽤来记录当前栈顶的位置
⽤代码实现:
function Stack(){
this.itemArr=[];
this.top=0;//初始化栈顶位置为0
}
Stack.prototype={
push:function(el){
return this.itemArr[this.top++]=el;
},
pop:function(){
return this.itemArr.splice(--this.top,1)
},
peek:function(){
return this.itemArr[this.top-1];
},
size:function(){
return this.top;
},
clear:function(){
this.top=0;
this.itemArr=[];
return this.itemArr;
}
}
var arr=new Stack();。

js 拷贝 数组 方法

js 拷贝 数组 方法

js 拷贝数组方法在JavaScript中,有几种方法可以实现数组的复制。

下面我们将介绍其中的三种常用方法。

方法一:使用for循环遍历数组这是一种最基本的方法,通过使用for循环来遍历原始数组,并将每个元素复制到新数组中。

具体的代码如下所示:```javascriptlet originalArray = [1, 2, 3, 4, 5];let copiedArray = [];for (let i = 0; i < originalArray.length; i++) {copiedArray[i] = originalArray[i];}```上述代码中,我们首先声明了一个原始数组originalArray和一个空数组copiedArray。

然后,我们使用for循环遍历原始数组,将每个元素复制到新数组中。

这样,我们就得到了一个与原始数组相同的新数组。

方法二:使用Array.from()方法Array.from()方法是ES6中引入的一个新方法,它可以将类数组对象或可迭代对象转换为一个新的数组实例。

我们可以利用这个方法来复制一个数组。

具体的代码如下所示:```javascriptlet originalArray = [1, 2, 3, 4, 5];let copiedArray = Array.from(originalArray);```上述代码中,我们直接使用Array.from()方法将原始数组originalArray复制到新数组copiedArray中。

这样,我们就实现了数组的复制。

方法三:使用扩展运算符(...)扩展运算符(...)是ES6中引入的一个新语法,它可以将一个数组转换为用逗号分隔的参数序列。

我们可以利用这个语法来复制一个数组。

具体的代码如下所示:```javascriptlet originalArray = [1, 2, 3, 4, 5];let copiedArray = [...originalArray];```上述代码中,我们使用扩展运算符(...)将原始数组originalArray 展开,并将展开的元素赋值给新数组copiedArray。

浅谈js编写堆栈

浅谈js编写堆栈

浅谈js编写堆栈学过汇编的朋友肯定觉得它的堆栈操作指令很好用吧:push ax ;ax入栈push bx ;bx入栈pop bx ;bx出栈pop ax ;ax出栈相比之下,C的堆栈操作让人有时被搞得一头雾水,至少我当时在学数据结构的时候就有些头晕,但当学到汇编时,才有些醒悟了,如果你不知道堆栈是什么,那么我就简单介绍一下:堆栈就像一个箱子,你往里面放东西,先放进去的东西在最底下,而最后放进去的东西在最顶上,有点类似是把东西叠起来的方式.堆栈的操作必须遵照"先进后出,后进先出"的原则,也就是你最后被放进的东西可以最先被取出,最先放进的东西只能在最后被取出.注意我一开始举的例子,ax最先被压入堆栈,所以它在最后才能出栈.这样说有些抽像是吧,那举个例子吧,用js语言来描述吧:var a=[1,2,3,4,5] //假设我们现在认为a是一个堆栈(堆栈最简单的构成方式就是用一个一维数组)如果我们使用堆栈的操作方法压入一个数据,比如是6,那么现在的堆栈是这样的:a=[1,2,3,4,5,6],这样数据就入栈了,继续这样操作我们再压了更多数据,现在堆栈变成这样了:a=[1,2,3,4,5,6,7,8,9],那么,如果现在要出栈的话,就必须先把9弹出,然后再把8弹出,如此类推.假设我们要取数据6,那我们就必须把它上面的数据7~9依次弹出去,这样6就成了栈顶的元素,才能正常出栈.有人会提出疑问,为什么我们不用a[5]来读取数据6,呵呵,很聪明的问题,我现在来回答你,的确,你可以用这样的访法读得你要的数据,不过,这样一来,就不是我们所说的堆栈操作了,想想堆栈操作的定义和它的操作原则,入栈时,数据存放在栈的最顶端,然后顶部指针递增,指向更高一个空的数据单元(忘了介绍头指针了,也就是指向栈顶数据的数组下标+1,指向堆栈中顶部的一个空闲单元,数据入栈时就存入这个单元中,然后头指针递增1,指向更高一个空闲单元),而出栈则相反,首先把顶部的数据弹出去,然后清空这个单元,头指针递减1,这样就释放掉了不需要的资源.操作的原则:先进后出,后进先出.现在来比较一下,用下标去直接访问数组,并不满足堆栈的定义和操作原则.现在清楚了吧,如果你想用堆栈,就别想着下标,尽管我们用数组来模拟堆栈,但是,堆栈并不等于数组.相信学过C的朋友知道,数组只是最简单的模拟堆栈的形式,更多的,可以用链表去实现堆栈,不过js没那功能,所以就用数组吧!明白了堆栈的原理,就开始写代码模拟堆栈吧,这里我用函数及其方法来实现,关于构造函数以及函数的方法的使用在javascript权威指南上有介绍,推荐大家去看看,记得网上流传着一个日历脚本,其实现也是用构造函数来完成的,因为那时对Array支持不够完善吧,所以用构造函数来实现数组,这是我猜想的,如果说错了大家见谅.好了,直接放代码了,有注释的,运行一下你就明白了.。

javascript关于赋值、浅拷贝、深拷贝的个人理解

javascript关于赋值、浅拷贝、深拷贝的个人理解

javascript关于赋值、浅拷贝、深拷贝的个⼈理解⼀、栈、堆、指针地址 栈内存:个⼈理解是,基本数据类型和引⽤数据类型都会⽤到的⼀个空间,这个空间以key-value形式存在,value本⾝不可修改,只能赋值替换; 堆内存:堆,就是堆积,每⼀个被开辟的空间可以想象成⼀个空纸盒⼦,纸盒⼦所在的纸盒⼦堆就是 “堆” 。

基本数据类型没有堆的概念。

堆,只针对引⽤数据类型。

存储⽅式应该是以对象(object)形式保存,对象内容包含key-value形式数据,value本⾝同样不可修改,只能赋值替换; 指针地址:针对引⽤数据类型在栈保存的值就是指针地址,地址指向保存在堆⾥⾯的对象。

⼆、赋值 赋值分两个,⼀个是基本数据类型的赋值,⼀个是引⽤数据类型的赋值,基本数据类型赋的是 “值”,引⽤数据类型赋的是“指针地址”。

1.基本数据类型赋值//在栈内开辟⼀个空间,空间名称叫a,存放值1;var a = 1;//在栈内开辟⼀个空间,空间名字叫b。

接着先把a的值1复制⼀份,然后存放进bvar b = a;如下图:2.引⽤数据类型赋值//⾸先在栈开辟⼀个空间a存放指针地址,设指针地址为address1;同时会在堆⾥⾯开辟⼀个空间放置对象数据 2 var a = {no: 1,per: {name: "jack"},per2: {name: "rose"}}//a赋值给b,此时b会在栈开辟⼀个空间b,⽤来放置address1,这个指针指向a所在堆的对象数据var b = a;//修改赋值后的值b,其实就是修改b的指针address1所指向的对象数据b.no = 1314;//修改b会影响原数据(所有层次的数据都会影响)//这个原数据其实不是原数据,因为a和b其实都是同⼀个数据//就像从中国去美国,可以从a地点(⽐如北京)或者b地点(⽐如上海)坐飞机去,但是到达的都是同⼀个地⽅(也就是对象数据) = "王五";console.log(a, b)上⾯代码打印如图:对b的修改会影响a原本的值。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

关于JS堆栈与拷贝
1、栈(stack)和堆(heap)
stack为自动分配的内存空间,它由系统自动释放;而heap则是动态分配的内存,大小不定也不会自动释放。

2、基本类型和引用类型
基本类型:存放在栈内存中的简单数据段,数据大小确定,内存空间大小可以分配。

5种基本数据类型有Undefined、Null、Boolean、
Number和String,它们是直接按值存放的,所以可以直接访问。

引用类型:存放在堆内存中的对象,变量实际保存的是一个指针,这个指针指向另一个位置。

每个空间大小不一样,要根据情况开进行特定的分配。

当我们需要访问引用类型(如对象,数组,函数等)的值时,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据。

3、传值与传址
前面之所以要说明什么是内存中的堆、栈以及变量类型,实际上是为下文服务的,就是为了更好的理解什么是“浅拷贝”和“深拷贝”。

基本类型与引用类型最大的区别实际就是传值与传址的区别。

测试用例:
从上面我们可以得知,当我改变b中的数据时,a中数据也发生了变化;但是当我改变c的数据值时,a却没有发生改变。

这就是传值与传址的区别。

因为a是数组,属于引用类型,所以它赋予给b 的时候传的是栈中的地址(相当于新建了一个不同名“指针”),而不是堆内存中的对象。

而c仅仅是从a堆内存中获取的一个数据值,并保存在栈中。

所以b 修改的时候,会根据地址回到a堆中修改,c则直接在栈中修改,并且不能指向a堆内存中。

3、浅拷贝
前面已经提到,在定义一个对象或数组时,变量存放的往往只是一个地址。

当我们使用对象拷贝时,如果属性是对象或数组时,这时候我们传递的也只是一个地址。

因此子对象在访问该属性时,会根据地址回溯到父对象指向的堆内存中,即父子对象发生了关联,两者的属性值会指向同一内存空间。

alert(b.key3); //33333
alert(a.key3); //undefined
a对象中key1属性是字符串,key2属性是数组。

a拷贝到b,12属性均顺利拷贝。

给b对象新增一个字符串类型的属性key3时,b能正常修改,而a 中无定义。

说明子对象的key3(基本类型)并没有关联到父对象中,所以undefined。

1 b.key2.push("大辉");
2 alert(b.key2); //小辉,小辉,大辉
3 alert(a.key2); //小辉,小辉,大辉
但是,若修改的属性变为对象或数组时,那么父子对象之间就会发生关联。

从以上弹出结果可知,我对b对象进行修改,a、b的key2属性值(数组)均发生了改变。

其在内存的状态,可以用下图来表示。

原因是key1的值属于基本类型,所以拷贝的时候传递的就是该数据段;但是key2的值是堆内存中的对象,所以key2在拷贝的时候传递的是指向key2对象的地址,无论复制多少个key2,其值始终是指向父对象的key2对象的内存空间。

4、深拷贝
或许以上并不是我们在实际编码中想要的结果,我们不希望父子对象之间产生关联,那么这时候可以用到深拷贝。

既然属性值类型是数组和或象时只会传址,那么我们就用递归来解决这个问题,把父对象中所有属于对象的属性类型都遍历赋给子对象即可。

测试代码如下:
alert(a.key2); //小辉,小辉
由上可知,修改b的key2数组时,没有使a父对象中的key2数组新增一个值,即子对象没有影响到父对象a中的key2。

其存储模式大致如下:。

相关文档
最新文档