Skip to content

数据类型 1 ​

本节目标:理解“数据类型 1”的核心思路,并能把它用于实际开发或面试表达。

说明:原 OneNote 中有图片引用,但图片未包含在导出目录中;本页保留了可用的文字与代码内容。

**1****、栈****(stack)****和堆(****heap****)**
==stack====为自动分配的内存空间,它由系统自动释放;而====heap====则是动态分配的内存,大小也不一定会自动释放==
**2****、数据类型**
==JS====分两种数据类型:==
**基本数据类型**==:==**Number****、****String****、****Boolean****、****Null****、** **Undefined****、****Symbol****(****ES6****),**==这些类型可以直接操作保存在变量中的实际值。==
**引用数据类型**==:==**Object****(在****JS****中除了基本数据类型以外的都是对象,数据是对象,函数是对象,正则表达式是对象)**
**3****、基本数据类型(存放在****栈****中)**
     ==基本数据类型是指存放在==**栈**==中的==**简单数据段,****数据大小确定,内存空间大小可以分配,**==它们是直接按值存放的,所以可以直接==**按值访问**
var a = 10;
var b = a;
b = 20;
console.log(a); // 10值
console.log(b); // 20值
==下图演示了这种基本数据类型赋值的过程:==

4**、引用数据类型(存放在堆内存中的对象,每个空间大小不一样,要根据情况进行特定的配置)** ==引用类型是存放在堆内存中的对象,变量其实是保存的在栈内存中的一个指针(保存的是堆内存中的引用地址),这个指针指向堆内存。== ==引用类型数据在栈内存中保存的实际上是对象在堆内存中的引用地址。通过这个引用地址可以快速查找到保存中堆内存中的对象==

[](javascript:void(0)😉

var obj1 = new Object();
var obj2 = obj1;
obj2.name = "我有名字了";
console.log(obj1.name); // 我有名字了

[](javascript:void(0)😉

==说明这两个引用数据类型指向了同一个堆内存对象。====obj1====赋值给====obj2====,实际上这个堆内存对象在栈内存的引用地址复制了一份给了====obj2====,但是实际上他们共同指向了同一个堆内存对象,所以修改====obj2====其实就是修改那个对象,所以通过====obj1====访问也能访问的到。==

```
1
2
3
4
5
6
7
8
9
10
text
|```
var a = [1,2,3,4,5];
var b = a;//传址 ,对象中传给变量的数据是引用类型的,会存储在堆中;
var c = a[0];//传值,把对象中的属性/数组中的数组项赋值给变量,这时变量C是基本数据类型,存储在栈内存中;改变栈中的数据不会影响堆中的数据
alert(b);//1,2,3,4,5
alert(c);//1
//改变数值
b[4] = 6;
c = 7;
alert(a[4]);//6
alert(a[0]);//1
```text
|

==从上面我们可以得知,当我改变====b====中的数据时,====a====中数据也发生了变化;但是当我改变====c====的数据值时,====a====却没有发生改变。==
     ==这就是==**传值与传址的区别**==。因为====a====是==**数组**==,属于==**引用类型**==,所以它赋予给====b====的时候传的是栈中的地址(相当于新建了一个不同名“指针”),而不是堆内存中的对象。而====c====仅仅是从====a====堆内存中获取的一个数据值,并保存在栈中。所以====b====修改的时候,会根据地址回到====a====堆中修改,====c====则直接在栈中修改,并且不能指向====a====堆内存中。==

**5****、浅拷贝**
==前面已经提到,在定义一个对象或数组时,变量存放的往往只是一个地址。当我们使用对象拷贝时,如果==**属性是对象或数组时**==,这时候我们传递的也只是一个地址。因此子对象在访问该属性时,会根据地址回溯到父对象指向的堆内存中,即==**父子对象发生了关联**==,两者的属性值会指向同一内存空间。==

[](javascript:void\(0\);)

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)//11111alert(b.key3)//33333alert(a.key3);//undefined


[](javascript:void\(0\);)

b.key2.push("大辉")alert(a.key2);//小辉,小辉,大辉
==但是若是修改的属性变为对象或数组时,那么父子对象之间就发生关联,从上可知:==

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

[](javascript:void\(0\);)

//ES6实现浅拷贝的方法var a = {name:"暖风"}var b= Object.assign({},a);b.age = 18;console.log(a.age);//undefined----------------------------------//数组var a = [1,2,3];var b = a.slice();b.push(4);b//1,2,3,4a//1,2,4----------------------------------var a = [1,2,3];var b = a.concat();b.push(4);b//1,2,3,4a//1,2,4----------------------------------var a = [1,2,3];var b = [...a]b//1,2,3,4a//1,2,4


[](javascript:void\(0\);)

**6****、深拷贝**
==或许以上并不是我们在实际编码中想要的结果,我们==**不希望父子对象之间产生关联**==,那么这时候可以用到==**深拷贝**==。既然属性值类型是数组和或象时只会传址,那么我们就用==**递归**==来解决这个问题,把父对象中所有属于对象的属性类型都遍历赋给子对象即可。测试代码如下:==

[](javascript:void\(0\);)

var a={key1:"11111"}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("大辉");b.key2//小辉,小辉,大辉a.key2//小辉,小辉


[](javascript:void\(0\);)

==最后:== **总结基本数据类型和引用数据类型区别**
**1****、声明变量时内存分配不同**
==*====原始类型:在栈中,因为占据空间是固定的,可以将他们存在较小的内存中====-====栈中,这样便于迅速查询变量的值==
==*====引用类型:存在堆中,栈中存储的变量,只是用来查找堆中的引用地址。==
   ==这是因为:引用值的大小会改变,所以不能把它放在栈中,否则会降低变量查寻的速度。相反,放在变量的栈空间中的值是该对象存储在堆中的地址。地址的大小是固定的,所以把它存储在栈中对变量性能无任何负面影响==
**2****、不同的内存分配带来不同的访问机制**
    ==在====javascript====中是不允许直接访问保存在堆内存中的对象的,所以在访问一个对象时,首先得到的是这个对象在堆内存中的地址,然后再按照这个地址去获得这个对象中的值,这就是传说中的==**按引用访问**==。==
    ==而原始类型的值则是可以直接访问到的。==
**3****、复制变量时的不同**
 ==1====)原始值:在将一个保存着原始值的变量复制给另一个变量时,会将原始值的副本赋值给新变量,==**此后这两个变量是完全独立的,他们只是拥有相同的****value****而已。**
==2====)引用值:在将一个保存着对象内存地址的变量复制给另一个变量时,会把这个内存地址赋值给新变量,==
也就是说这两个变量都指向了堆内存中的同一个对象,他们中任何一个作出的改变都会反映在另一个身上。
(这里要理解的一点就是,复制对象时并不会在堆内存中新生成一个一模一样的对象,只是多了一个保存指向这个对象指针的变量罢了)。**多了一个指针**
**4****、参数传递的不同(把实参复制给形参的过程)**
首先我们应该明确一点:ECMAScript中所有函数的参数都是按值来传递的。
但是为什么涉及到原始类型与引用类型的值时仍然有区别呢?还不就是因为内存分配时的差别。
1)原始值:只是把变量里的值传递给参数,之后参数和这个变量互不影响。
2)引用值:对象变量它里面的值是这个对象在堆内存中的内存地址,这一点你要时刻铭记在心!
因此它传递的值也就是这个内存地址,这也就是为什么函数内部对这个参数的修改会体现在外部的原因了,因为它们都指向同一个对象。
 \> 来自

https://www.cnblogs.com/c2016c/articles/9328725.html

本站总访问 — 次