八股文卷出了新高度,这道阿里的连续赋值面试题确实有点东西~

505 阅读4分钟

前言

Hello~大家好。我是秋天的一阵风

最近,在刷短视频的闲暇时光里,我意外地刷到了一道关于连续赋值的编程面试题。

起初,我以为这只是一道简单的考查引用数据类型的题目,但当我看到答案时,却感到有些困惑,甚至可以说是摸不着头脑。所以借这篇文章记录一下这道有意思的题目和探究过程,并且分享给大家。

我们话不多说,直接上题目。

一、先看题目

var a = { n: 1 };
var b = a;
a.x = a = { n: 2 };

console.log(a.x);  // undefined
console.log(b.x);  // {n: 2}

我将打印答案也一并附上,给大家十分钟时间,大家可以先试着分析,看与答案能否对的上。

二、回顾基础

我们在分析答案之前先回顾一下javascript中的基础知识,同学们也可以趁这个时候进行查漏补缺:

1. 原始数据类型和引用数据类型

(1)在JavaScript中,数据类型可以分为两大类:原始数据类型(Primitive Types)和引用数据类型(Reference Types)。

(2) 原始数据类型包括字符串(String)数字(Number)布尔值(Boolean)nullundefined和ES6中引入的符号(Symbol)

(3) 引用数据类型则包括对象(Object)、数组(Array)、函数(Function)等。

2. 原始数据类型的赋值过程

对于原始数据类型,赋值是通过值传递完成的。这意味着当你将一个原始类型的值赋给一个变量时,实际上是创建了这个值的一个副本。例如:

let a = 10; // a 指向值 10
let b = a;  // b 也指向值 10

3. 引用数据类型的赋值过程

对于引用数据类型,赋值是通过引用传递完成的。这意味着当你将一个引用类型的值赋给一个变量时,你实际上是在创建一个指向内存中相同位置的新的引用。例如:

let obj1 = { name: 'Kimi' }; // obj1 指向一个对象
let obj2 = obj1;            // obj2 也指向同一个对象,不是一个新的对象

当使用var(或letconst)声明一个变量并将其设置为一个对象时,这个变量实际上存储的是一个指向该对象内存地址的引用

在这个例子中,obj1obj2 都指向内存中的同一个对象。因此,如果你通过 obj2 修改了对象的属性,obj1 指向的对象也会发生相应的变化,因为它们指向的是同一个对象。

tips: 如果你对于引用对象的深拷贝方式感兴趣,可以看看我的这篇文章~:

告别老套!structuredClone:新一代深拷贝神器,让JSON.parse和JSON.stringify靠边站!

二、逐步分析

回顾完基础知识后,我们来一步步地分析打印的输出结果。

var a = { n: 1 };

  • 这行代码创建了一个对象字面量,其中包含一个属性n,其值为1
  • JavaScript引擎在内存的堆(heap)区域为这个对象分配空间。
  • 然后,引擎在栈(stack)区域为变量a分配空间,并在其中存储一个指向堆中对象的引用(内存地址)。

如图所示:

image.png

var b = a;

  • 这行代码创建了另一个变量b
  • 由于a是一个对象的引用,b也被赋予了这个相同的引用。
  • 这意味着b也指向堆中与a相同的对象。
image.png

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

好了,这个题目最关键的就是第三行进行赋值。核心就是要搞清楚执行顺序。

首先会在a指向的引用对象中(也就是{n:1}),声明一个 x 属性,如图所示:

image.png

接着会创建一个新的引用对象,也就是{n:2},如图所示:

image.png

然后会将a的地址修改指向这个新的引用对象,如图所示:

image.png

最后一步,会将这个a的新地址赋值给之前的x属性中,如图所示:

image.png

这就是前三行代码执行完的最终结果,那么现在来看输出就一目了然了。

console.log(a.x);

a 此时指向的是{n:2} ,那么a.x 自然就是 undefined了。

console.log(b.x);

b 此时指向的是 { n: 1 , x: 00bb}, b.x 存的是 {n:2} 的地址,那么b.x自然就是 {n:2}

三、在线运行网站

我还找到了一个在线运行网站: pythontutor,方便同学们更加清晰地去了解执行过程。

image.png image.png
var a = { n: 1 };
var b = a;
a.x = a = { n: 2 };

console.log(a.x);  
console.log(b.x);