引用类型和值类型

来源:互联网 发布:网络中继器 编辑:程序博客网 时间:2024/05/14 05:25

在js中,值类型有:undefined、number、Boolean、string等基本类型

        引用类型有:函数、数组、json类型{“a”:5,”b”:20}等都是对象,都是引用类型

 

值类型内存分配和引用类型内存分配是不同的

值类型:创建一个变量,新建一个内存区域

引用类型:创建一个变量,内存中保存的是变量入口地址。

引用类型会分配两个区域,一个保存地址,一个保存内容。

变量赋值的区别:值类型赋值给一个变量,实际上变量创建了一个新的内存区域,它们两者的变化不会影响对方。

引用类型赋值给一个变量,实际上只是创建了一个区域,保存赋值的那个变量的入口地址。

值类型内存分配。

1、值类型内存分配

function chainStore()
{
    var store1='ouyang';
    var store2=store1;
    store1='fangfang';
    alert(store2); //ouyang
}
chainStore();

//把一个值类型(也可以叫基本类型)store1传递给另一个变量(赋值)时,其实是给store2分配了一块新的内存空间,因此改变store1的值对store2没有任何影响

 

2、引用类型内存分配

引用类型内存分配其实操作的是地址,所以修改任何一个变量都是修改自身

function chainStore()
{
      var store1=['ouyang'];
    var store2=store1;
    store1[0]='fangfang.';
    alert(store2[0]); //fangfang.
}
chainStore();

//在上面的代码中,store2只进行了一次赋值,理论上它的值已定,但后面通过改写store1的值,发现store2的值也发生了改变,这是因为之前的赋值只是给创建了一个地址,store2保存的是变量的入口地址,所以修改变量时,也修改了store2指向的内存里面的值。也是我们要注意的地方。

总结:值类型赋值,是重新分配了一个内存区域;引用类型赋值赋值其实赋的是指针

 

面试题目1 – 详细讲解

var a = {"x": 1};var b = a;a.x = 2;b.x; 
a = {"x":3};b.x;a.x = 4;b.x;详细解释一下每一个b.x的值是怎么来的?

首先定义一个对象a,有一个属性x,值为1。接着让b = a,这一步的结果就是ab指向了同一个对象。

在内存中,对象的存储和基本数据类型不同。基本数据类型直接保存在栈里,a =1b =1,在栈里会保存两份1,分别赋值给ab。修改ab,对另一个变量不会有什么影响。

对象则不然,变量ab如果被赋值对象,ab实际上保存的只是对象的地址,而且ab还是被存储在栈里,同时ab的地址是相同的。但对象是在堆里保存,且只保存一份,对象的地址就是ab的值,ab都指向同一个对象。这与C里面的指针类似,修改指向同一个对象的任何一个变量,与之引用同一对象变量很快就会发生同样的变化。如下图:

所以现在的情况就是,ab都指向了堆中的一个对象,这个对象的属性x值是1。那么a.x = 1b.x自然也等于1

a.x = 2接下来发生一件事情,a修改了对象的x属性为2,这个变化反映到了堆中:

看,ab还是指向了同一个对象,只不过对象中的x属性值变成了2。这一变化b很快就发现了,所以你再去访问b.x,实际上就是访问堆中的对象的x属性,也就是2

a = {"x":3};

再后来,为a赋值了一个新的对象,虽然它也有一个属性x,但它确实是一个新对象!那么内存堆中发生了什么呢?首先,堆中原有的对象(x = 2的那个)还在那里。因为新建了一个对象(x = 3的),堆中就会出现一个新的对象,与原来的对象毫无关系。同时,b并没有变化,它还指向原有的对象(x = 2),但a指向原来的对象的地址却发生了变化,它指向了x = 3的这个新对象。堆中情况如下:

如上图,a的地址变了,同时a和原来的对象也没有指向关系了,它指向了新的对象,这个新对象的x = 3。而b对象没有任何变化,它还坚守着自己的对象,对象的x属性是2

a.x = 4;

接着修改了ax属性为4,参考前面的描述,我们可以知道堆的变化如下:

瞧,ax属性变成了4,但ba已经没有关系了,所以bx属性还是2

 

PS:以上地址什么的,都是随便写的,实际内存中肯定不是这些数字。

面试题目2 – 重点讲解

代码如下:

<script>

var a = {n:1};

var b = a; 

a.x = a = {n:2};

console.log(a.x);// --> undefined

console.log(b.x);// --> [object Object]

</script>

上面的例子看似简单,但结果并不好了解,很容易把人们给想绕了——“a.x不是指向对象a了么?为啥log(a.x)是undefined?”、“b.x不是应该跟a.x是一样的么?为啥log出来居然有2个对象”

当然各位可以先自行理解一下,若能看出其中的原因和工作机理自然就无须继续往下看啦。

下面来分析下这段简单代码的工作步骤,从而进一步理解js引用类型“赋值”的工作方式。首先是

var a ={n:1};  var b = a;

在这里a指向了一个对象{n:1}(我们姑且称它为对象A),b指向了a所指向的对象,也就是说,在这时候a和b都是指向对象A的:

这一步很好理解,接着继续看下一行非常重要的代码a.x = a = {n:2};

我们知道js的赋值运算顺序永远都是从右往左的,不过由于“.”是优先级最高的运算符,所以这行代码先“计算”了a.x。

这时候发生了这个事情——a指向的对象{n:1}新增了属性x(虽然这个x是undefined的):

从图上可以看到,由于b跟a一样是指向对象A的,要表示A的x属性除了用a.x,自然也可以使用b.x来表示了。

接着,依循“从右往左”的赋值运算顺序先执行a={n:2} ,这时候,a指向的对象发生了改变,变成了新对象{n:2}(我们称为对象B):

接着继续执行 a.x=a,很多人会认为这里是“对象B也新增了一个属性x,并指向对象B自己”

但实际上并非如此,由于( .  运算符最先计算)一开始js已经先计算了a.x,便已经解析了这个a.x是对象A的x,所以在同一条公式的情况下再回来给a.x赋值,也不会说重新解析这个a.x为对象B的x。所以 a.x=a 应理解为对象A的属性x指向了对象B:

那么这时候结果就显而易见了。当console.log(a.x)的时候,a是指向对象B的,但对象B没有属性x。没关系,当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止。但当查找到达原型链的顶部 - 也就是 Object.prototype - 仍然没有找到指定的属性B.prototype.x,自然也就输出undefined;

而在console.log(b.x)的时候,由于b.x表示对象A的x属性,该属性是指向对象B,自然也输出了[object Object]了,注意这里的[object Object]可不是2个对象的意思,对象的字符串形式,是隐式调用了Object对象的toString()方法,形式是:"[object Object]"。所以[objectObject]表示的就只是一个对象罢了:)

面试题2

JavaScript值类型和引用类型有哪些 (1)值类型:数值、布尔值、null、undefined。 (2)引用类型:对象、数组、函数。

 

0 0
原创粉丝点击