连续赋值和内存指针的问题解析(a.x=b={n:2})

前几天偶然看到了一个这样的题:

1 <script>
2     var a={n:1};
3     var b=a;
4     a.x=a={n:2};//关键代码5     console.log(a.x);//undefined
6    console.log(b.x);//[object Object]
7 </script>

这个题代码不多,不过考察的知识点却非常不错。我们知道内存空间分为栈内存和堆内存。栈内存用来存放供js代码来执行的环境,所以为了保证性能减少内存占用,我们一般把占用空间较小的类似于基本数据类型放在栈内存中,像引用数据类型这种占据空间比较大的东西,我们需要将它放在一个贮藏室中—堆内存中,堆内存相较于栈内存对js执行时对性能影响很小,虽然引用类型存放在堆内存里了,但是当我们代码执行时我们也需要用到这部分,所以我们需要将类似于标签,名字(类似于元素的ID)放置于栈内存中,和堆内存中的东西一一对应,当执行到他时直接去堆内存中找,这种模式就好比古代的银票和同等价值的黄金和白银,一个很有钱的商人不能每次出门都推着一车银子吧,那可是金属,那这样有钱人活的就太累了,所以一个等面值的银票揣在手中,一切问题就搞定了。 所以“不同变量的同一引用数据类型赋值就是指向的是同一个堆内存空间,不会去创造新的内存空间,不然太浪费空间了,你要知道这可是内存,内存呀!!”

1. 像此题中一样 var a={n:1};    var b=a;  这是并没有给变量b在堆内存中在创建一个对象{n:1},而是他俩共同指向内存空间中的{n:1},不论你创建多少个变量去赋值堆内存中始只存在一个{n:1};我们姑且称呼这个对象为A,草图如下:

2. 接下来我们可以看到此题中有标注‘关键代码’字样的,他们考察了我们连续赋值,并且还有属性操作:

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

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

所以我们先进行属性操作,a.x,因为此时x作为a的一个属性,没有最开始没有从右向左赋值时它的值为undefined,这行代码可以说已经执行了a.x,这时如下图

接下来从右向左开始赋值,将a的指向从对象A指向了对象B{n:2},

接下来继续执行 a.x=a,很多人会认为这里是“对象B也新增了一个属性x,并指向对象B自己”,很多人这样认为是错误的,根据优先性( .  运算符最先计算),咱们刚才已经计算过a. x,那时变量a的指向还是对象A,变量a指针并没有发生变化,所以此时a.x确切地说应该是对象A.x,代码再次执行时,指针发生了变化,a的指针指向B,此时a.x=a再赋值时不再次执行依次a.x然后再赋值,这样系统不允许,而是直接赋值,相当于之前的A.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]"。

为了验证咱们的结果我们可以将上面的题进行变种:

1 <script>
2     var a={n:1};
3     var b=a;
4     a={n:2}
5     a.x=a;
6     console.log(a.x);//[object,Object]
7     console.log(b.x);//undefined

8 </script>

那么与上题的区别就是我们将变量a指针先改变,然后在进行对象属性的操作a.x。

嘿嘿,如果我们这样连等呢?

1 <script>
2     var a={n:1};
3     var b=a;
4     a=a.x={n:2};
5     console.log(a.x);
6     console.log(b.x);
7     console.log(a.n);
8     console.log(b.n);
9 </script>
 

此时的结果是和第一题的解题思路是一样的,只要是既有变量和变量属性的连等赋值的打印元素属性的值都为undefined

总结:所以我们做此类题时无外乎考虑这几个知识点连等赋值的优先级别从右向左,不过遇见属性一切玩完,

我验证了一下凡是类似于这样变量a=a.x=引用数据类型,都可以引用只要是既有变量和变量属性的连等赋值的打印元素属性的值都为undefined。

时间: 2024-10-23 12:38:17

连续赋值和内存指针的问题解析(a.x=b={n:2})的相关文章

VB.NET 内存指针和非托管内存的应用

介绍 Visual Basic 从来不像在C或C++里一样灵活的操纵指针和原始内存.然而利用.NET框架中的structures 和 classes,可以做许多类似的事情.它们包括 IntPtr,   Marshal 以及 GCHandle. 这些structures(结构) 和classes(类) 允许你在托管和非托管环境中进行交互.本文中将向您展示如何使用这些structures 和 classes 去完成指针和内存的操作. 关于 IntPtr 结构 IntPtr  结构的行为像一个整型指针

结构体指针内存——指针数组——字符串指针内存申请

前几天用的结构体,结构体内还包含有结构体指针和数组以及指向字符串的指针,发现自己对这方面的东西还很容易犯错,故现在讲其中容易出错的地方写出来,分享给大家也方便自己日后查看. typedef struct { char name[50]; char job[50]; int age; int people_id; } peopleInfo; typedef struct { bool typeAdd; bool typeDel; int length; peopleInfo *info; char

内存溢出与内存泄漏的简要解析

我们在实际编程中经常会说到内存溢出和内存泄漏,特别对于C/C++程序来说(以下代码示例均为C/C++),因为这时我们会跟内存直接打交道.然而很多时候我们并不能完全搞明白这两个概念,有时甚至会将二者颠倒混淆. 其实从命名上也能明白内存溢出和内存泄漏的大概,举个可能并不恰当的例子.好比是往水缸里打水,本来这个缸只能装下5桶水,第5桶装完你还硬要装第6桶,缸里的水自然就溢出来了,此为“内存溢出”:缸里打满水后并没有人用,第二天发现缸里的水少了一半,第三天一滴不剩了,原来是缸底打了个洞忘补了(为什么要在

JS变量、作用域、 内存(含 预解析面试题)

一.变量 1. 变量 命名: 变量名以$.字母.下划线开头 驼峰命名法 变量名要有意义 2. 变量 声明 声明多个变量:var message, name, age; 重复声明无效,只看第一次声明[特殊:函数内形参 与 函数内的函数名(函数声明方式创建的函数) 重名,以函数名声明为准] <script> console.log(typeof a); function a() {} var a = 10; </script> // 输出:function <script>

指针和数组解析

指针和数组 指针和数组是一样的吗? 经常可以看到“数组就是指针”.“数组名就是常量指针”这些说法,但真的是这样吗?让我们先看一下指针和数组的定义. 1. 指针 根据C99标准,指针的定义如下: A pointer type may be derived from a function type or an object type, called the referenced type. A pointer type describes an object whose value provides

Linux 内存使用方法详细解析

我是一名程序员,那么我在这里以一个程序员的角度来讲解Linux内存的使用. 一提到内存管理,我们头脑中闪出的两个概念,就是虚拟内存,与物理内存.这两个概念主要来自于linux内核的支持. Linux在内存管理上份为两级,一级是线性区,类似于00c73000-00c88000,对应于虚拟内存,它实际上不占用实际物理内存:一级是具体的物理页面,它对应我们机器上的物理内存. 这里要提到一个很重要的概念,内存的延迟分配.Linux内核在用户申请内存的时候,只是给它分配了一个线性区(也就是虚存),并没有分

Linux内存使用方法详细解析

我是一名程序员,那么我在这里以一个程序员的角度来讲解Linux内存的使用. 一提到内存管理,我们头脑中闪出的两个概念,就是虚拟内存,与物理内存.这两个概念主要来自于linux内核的支持. Linux在内存管理上份为两级,一级是线性区,类似于00c73000-00c88000,对应于虚拟内存,它实际上不占用实际物理内存:一级是具体的物理页面,它对应我们机器上的物理内存. 这里要提到一个很重要的概念,内存的延迟分配.Linux内核在用户申请内存的时候,只是给它分配了一个线性区(也就是虚存),并没有分

内存对齐问题的解析

存储器的内存布局一般都是对齐的,即是按字对齐或半字对齐的方式访问的,优点是可以避免内存的浪费同时也有很高的内存操作和数据处理的速度. 如果访问非对齐的内存数据: 产生不可预测的结果: 忽略造成访问不对齐的低地址位: 上一步后,使这些低地址位控制装载数据循环后移. 先看下面这些问题,你都知道这些结构体所占内存的大小以及结构体成员的实际内存分布吗? 结构体所占内存和实际使用内存是两个完全不同的概念, 结构体所占内存大小可以使用sizeof(type)测出,而实际使用的内存大小必须通过offsetof

Cocos2d之&ldquo;引用计数&rdquo;内存管理机制实现解析

一.引言 本文主要分析cocos2d游戏开发引擎的引用计数内存管理技术的实现原理.建议读者在阅读本文之前阅读笔者之前一篇介绍如何使用cocos2d内存管理技术的文章--<Cocos2d之Ref类与内存管理使用详解>. 二.相关概念 引用计数 引用计数是计算机编程语言的一种内存管理技术,是指将资源(对象.内存或者磁盘空间等)的被引用计数保存起来,当引用计数变为零时就将资源释放的过程.使用引用计数技术可以实现自动内存管理的目的. 当实例化一个类时,对象的引用计数为1,在其他对象需要持有这个对象时,