关于js数据类型中“基本类型”与“引用类型”的区别
JavaScript变量是松散类型,这就决定了它只是在特定时间用于保存特定值的一个名字而已,所以在运行期间对基本数据类型和引用类型的变量使用时要特别注意。
1. 基本类型和引用类型的值及使用有什么区别?
基本类型值:指的就是简单的数据段;
引用类型值:指的是那些可能由多个值构成的对象;
将一个值赋给变量时,解析器必须确定这个值是基本类型值(Undefined,Null,Boolean,String,Number),还是引用类型值;(其他语言String是以对象的形式表示,ECMAScript放弃了这一传统)
基本数据类型是按值访问的,因此可以操作保存在变量中的实际的值;
引用类型的值是保存在内存中的对象,与其他语言不同,JavaScript不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。在操作对象时,实际上是操作对象的引用而不是实际的对象,因此,引用类型的值是按引用访问的。
2. 基本类型与引用类型对动态属性的支持的区别有哪些?
定义基本类型值和引用类型值的方式是类似的:创建一个变量并为该变量赋值;
当这个值保存到变量之后,对不同类型值可以执行的操作则大相径庭。对于引用类型的值,我们可以为其添加属性和方法,也可以改变和删除其属性和方法,例如:
var person = new Object();
person.name = "Jack";
console.log(person.name); // "Jack"
我们不能给基本类型的值添加属性,
var name = "Jack";
name.age = 27;
console.log(name.age); // undefined
3. 基本类型与引用类型在复制变量值及内存分配上的区别?
除保存方式不同外(一个在栈内存,一个在堆内存),在复制变量值也存在着不同:
在复制基本类型值时,基本类型会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上,如下示例:
var a= 10;
var b= a;
a ++ ;
console.log(a); // 11
console.log(b); // 10
在此,a中保存的值是10,当使用a的值来初始化b时,b中也保存了值10,但两者中的10是完全独立的,b中的值只是a中10的一个副本。此后两个变量参与的任何操作都不会相互影响。
当从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间中。不同的是,这个值的副本实际上是一个指针,而这个指针指向存储在堆内存中的一个对象,复制操作结束后,两个变量实际上将引用同一个对象。因此,改变其中一个变量,就会影响另一个变量。
var a = {}; // a保存了一个空对象的实例
var b = a; // a和b都指向了这个空对象
a.name = 'jozo';
console.log(a.name); // 'jozo'
console.log(b.name); // 'jozo'
b.age = 22;
console.log(b.age);// 22
console.log(a.age);// 22
console.log(a == b);// true
4. 基本类型与引用类型作为参数传递上的区别?
ECMAScript中所有函数的参数都是按值传递,也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。
在向参数传递基本类型值时,被传递的值会被复制给一个局部变量(即命名参数,或者用ECMAScripot的概念来说,就是arguments对象中的一个元素),当对局部变量进行操作时,局部变量的变化会反应在函数内部,并不会影响函数外部的值。
function add(b){
b+= 10;
return b;
}
var a= 10;
var result = add(a);
console.log(a); //10
console.log(result); //20
在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反应在函数的外部。
function setName(obj){
obj.name = "Jack";
}
var person = new Object();
setName(person);
console.log(person.name); //"Jack"
以上代码创建了一个对象,并将其引用保存在了变量person中,然后这个变量被传递到了setName()函数中之后就被复制给了obj。
在这个函数内部,obj和person引用的是同一个对象,于是在函数内部为obj添加name属性后,函数外部的person也将有所反应,因为person指向的对象在堆内存中只有一个。
5. 基本类型与引用类型在使用检测类型符号上的区别?
使用 typeof 操作符来检测基本类型值;
var num = 1;
var a = 'a';
var b;
var flag = true;
var o = null;
console.log(typeof num); //number
console.log(typeof a); //string
console.log(typeof b); //undefined
console.log(typeof flag); //boolean
console.log(typeof o); //object
使用 instanceof 操作符来检测引用类型值;
var a = [1,2,3];
console.log(a instanceof Object); //true
console.log(a instanceof Array); //true
console.log(a instanceof RegExp); //false
扩展1:内存中的存储区域
值类型存储在栈中,引用类型存储在堆中。
内存中是分为两个区域的;
一个是栈:它就是专门存放值类型的,但是它有一定的存储空间,只能存放基本数据类型的数据和对象类型的引用地址也叫哈希码。存储在栈里面的基本数据类型的值都是有最大值和最小值的,不能超出它的默认范围;
二就是堆:它的存储空间大,是用来存储“数组类型”和“对象类”的数据的。存储在堆里的引用类型数据是没有固定大小的,比如说一个对象类型的数据,你可以往里面存放一个字符、两个字符·····更多,不管你存多少它都会把你存放的数据在内存的堆里面开辟一块空间来存储,在栈里面开辟一块空间来存放引用地址。
扩展2:值传递与引用传递
值传递和引用传递,属于函数调用时参数的求值策略(Evaluation Strategy),这是对调用函数时,求值和传值方式的描述,而非传递内容的类型(内容指:是值类型还是引用类型);
值类型 / 引用类型,是用于区分两种内存分配方式,值类型在调用栈上分配,引用类型在堆上分配;
一个描述内存分配方式,一个描述参数求值策略,两者之间无任何依赖或约束关系。
区别 | 值传递 | 引用传递 |
---|---|---|
会创建副本(copy) | 不创建副本 | |
函数中无法改变原始对象 | 函数中可以改变原始对象 |
对于值传递,无论是值类型还是引用类型,都会在调用栈上创建一个副本,不同是,对于值类型而言,这个副本就是整个原始值的复制。而对于引用类型而言,由于引用类型的实例在堆中,在栈上只有它的一个引用(一般情况下是指针),其副本也只是这个引用的复制,而不是整个原始对象的复制。
这便引出了值类型和引用类型(这不是在说值传递)的最大区别:值类型用做参数会被复制,但是很多人误以为这个区别是值类型的特性。其实这是值传递带来的效果,和值类型本身没有关系。只是最终结果是这样。
参考:
https://blog.csdn.net/wkyseo/article/details/51484506
https://jingyan.baidu.com/article/597a064326352b312b5243d0.html
上一篇: C#学习之:数据类型
下一篇: Numpy的数据类型及自定义数据类型