面试题
曾经有这样一道面试题:在js的全局环境下分别有一个变量a和变量b,其中变量a赋值为:{n: 1},然后让b = a,紧接着在让a.x=a={n: 2},最后问a.x和b的值分别是多少,代码如下:
var a = {n: 1};
var b = a;
a.x = a = {n: 2}
console.log(a.x);
console.log(b);
其实这道题主要考察两个点:
- 对象的存储位置(堆内存)
- 连等赋值是成员访问的优先级
掌握了这2点后相信这道题的答案也就很容易知道了。接下来我们从js的底层运行机制开始来分析一下这道题:
- 首先开辟一块栈内存(执行环境栈ECStack)来供代码执行
- 然后会形成一个全局上下文EC(G),用来执行全局的代码,比如:这个题目中的,创建变量a和b、连等赋值和两个console.log输出变量的值。这些代码都是在全局上下文区域中执行。
- 在执行到var a = {n: 1}时,首先是开辟一块堆内存0x000000,将对象{n:1}保存在该堆中,然后是创建变量a并保存在全局对象VO中,最后让变量a和堆内存的地址关联在一起。
- 代码继续向下执行(var b = a),这时发现对应的值已经创建,则直接创建变量b,并让b指向a
- 代码继续执行a.x = a = {n: 2}
- 在一般情况下的连等赋值时(例如“var a = b = 10”),正常都是从右向左进行处理,那么从右到左就是:b = 10, a = b 或 a = 10。
- 但是也有特殊情况,例如“ a.x = b = 10或 b = a.x = 10”,这种情况无论是b在前还是b在后面,都要优先计算a.x=10,,因为它是成员访问,而成员访问的优先级比价高(成员访问的优先级是19,仅次于小括号,关于成员访问优先级的问题请参考MDN)因此在执行到这句代码时的步骤是:首先开辟一个新的堆内存0x000001并将{n: 2}存在该堆中,然后再在上一步开辟的堆0x000000中再创建一个x并让x指向新的堆0x000001,最后再让a重新指向新的堆0x000001。
- 所以在最后输出a.x时是undefined因为0x000001中没有x,而输出b时值为{n:1,x:{n:2}}。
这样描述下来可能还不是很直观,下面我们用一组动画来演示一下。也可点击下图右上角的“码上掘金”查看完整动画演示 https://code.juejin.cn/pen/7091961682578620424
动画演示
动画演示的具体原理就不在这里描述了,主要就是靠一组settimeout和css动画,每隔1或1.5秒执行一个动画,比较简单就直接上代码了。也可以通过上面的码上掘金查看完整动画。
<div class="computer-box">
<div class="ec-stack">
<div class="header">
执行环境栈(ECStack) 栈内存:供代码执行 & 存储基本数据类型值
</div>
</div>
<div class="function">
<div class="header">全局执行上下文</div>
<div class="vo">VO(G)全局变量对象</div>
<div class="a">
<div class="name name-a">a</div>
<div class="line line-a"></div>
<div class="addr addr-a">0x000000</div>
</div>
<div class="b">
<div class="name name-b">b</div>
<div class="line line-b"></div>
<div class="addr addr-b">0x000001</div>
</div>
<div class="split"></div>
<div class="code">var a = { n: 1 }</div>
<div class="code">var b = a</div>
<div class="code">a.x = a = { n: 2 }</div>
<div class="code">console.log(a.x)</div>
<div class="code undefined">undefined</div>
<div class="code">console.log(b)</div>
<div class="code b-value">{n: 1, x: {n: 2}}</div>
<div class="step"><img src="/public/step.png" /></div>
</div>
<div class="dui dui-1">
<div class="header">堆内存:0x000000</div>
<div class="code">n: 1</div>
<div class="code code-addr">x: 0x000001</div>
<div class="line dui-1_line"></div>
</div>
<div class="dui dui-2">
<div class="header">堆内存:0x000001</div>
<div class="code">n: 2</div>
</div>
</div>
const ec_stack = document.querySelector(".ec-stack"),
el_function = document.querySelector(".function"),
el_step = document.querySelector(".step"),
el_namea = document.querySelector(".name-a"),
el_linea = document.querySelector(".line-a"),
el_addra = document.querySelector(".addr-a"),
el_nameb = document.querySelector(".name-b"),
el_lineb = document.querySelector(".line-b"),
el_addrb = document.querySelector(".addr-b"),
el_dui1 = document.querySelector(".dui-1"),
el_dui2 = document.querySelector(".dui-2"),
el_code_addr = document.querySelector(".code-addr"),
el_undefined = document.querySelector(".undefined"),
el_b_value = document.querySelector(".b-value"),
el_dui_1_line = document.querySelector(".dui-1_line");
// setTimeout(() => {
// el_function.style = "left:-420px;";
// el_dui1.style = "opacity: 0 !important;";
// el_dui2.style = "opacity: 0 !important;";
// ec_stack.style = "opacity: 0 !important;";
// }, 21000);
setTimeout(() => {
el_b_value.classList.add("opacity-anim");
clearTimeout();
}, 19500);
setTimeout(() => {
el_step.style = "transform: translateY(175px);";
}, 18500);
setTimeout(() => {
el_undefined.classList.add("opacity-anim");
}, 17500);
setTimeout(() => {
el_step.style = "transform: translateY(105px);";
}, 16500);
setTimeout(() => {
el_addrb.classList.add("opacity-anim");
el_linea.style =
"transform:rotate(10deg) translateY(30px);border-color:red;";
}, 15500);
setTimeout(() => {
el_dui_1_line.classList.add("opacity-anim");
el_dui_1_line.classList.add("line-width");
}, 14500);
setTimeout(() => {
el_code_addr.classList.add("opacity-anim");
}, 13500);
setTimeout(() => {
el_dui2.classList.add("opacity-anim");
}, 12500);
setTimeout(() => {
el_step.style = "transform: translateY(70px);";
}, 11500);
setTimeout(() => {
el_lineb.classList.add("opacity-anim");
el_lineb.classList.add("line-width");
}, 10500);
setTimeout(() => {
el_nameb.classList.add("opacity-anim");
}, 9500);
setTimeout(() => {
el_step.classList.add("step-anim");
}, 8500);
setTimeout(() => {
el_linea.classList.add("opacity-anim");
el_linea.classList.add("line-width");
}, 7500);
setTimeout(() => {
el_namea.classList.add("opacity-anim");
el_addra.classList.add("opacity-anim");
}, 6500);
setTimeout(() => {
el_dui1.classList.add("opacity-anim");
}, 5500);
setTimeout(() => {
el_step.classList.add("opacity-anim");
}, 4500);
setTimeout(() => {
el_function.classList.add("fun-left");
}, 3500);
setTimeout(() => {
el_function.classList.add("opacity-anim");
}, 2000);
setTimeout(() => {
ec_stack.classList.add("opacity-anim");
}, 1000);
.computer-box {
position: relative;
margin-top: 5px;
box-sizing: border-box;
border: 1px solid #606266;
width: 100%;
height: 700px;
padding: 5px;
}
.computer-box .ec-stack {
box-sizing: border-box;
width: 400px;
height: 100%;
border: 1px solid #606266;
opacity: 0;
transition: all 1s;
}
.computer-box .function {
box-sizing: border-box;
position: absolute;
width: 380px;
height: 450px;
border: 1px solid #606266;
bottom: 10px;
left: 420px;
opacity: 0;
transition: all 1s;
}
.computer-box .step {
position: absolute;
left: 150px;
top: 125px;
opacity: 0;
transition: all 1s;
}
.computer-box .function.fun-left {
left: 15px;
}
.computer-box .header {
height: 20px;
width: 100%;
border-bottom: 1px solid #606266;
color: #c0c4cc;
text-align: center;
font-size: 12px;
}
.computer-box .a,
.computer-box .b {
box-sizing: border-box;
height: 35px;
line-height: 35px;
text-align: center;
display: flex;
justify-content: center;
transition: all 1s;
}
.computer-box .line {
border-top: 1px solid #000;
transform: translateY(18px);
width: 0px;
transition: all 1s;
opacity: 0;
}
.computer-box .line-b {
transform: rotate(-10deg);
transition: all 1s;
}
.name {
width: 20px;
opacity: 0;
transition: all 1s;
}
.addr {
width: 100px;
opacity: 0;
transition: all 1s;
}
.split {
height: 1px;
border-bottom: 1px solid #000;
}
.code {
box-sizing: border-box;
height: 35px;
line-height: 35px;
margin-left: 20px;
}
.dui {
box-sizing: border-box;
position: absolute;
width: 150px;
height: 100px;
border: 1px solid orange;
border-radius: 5px;
top: 10px;
left: 420px;
opacity: 0;
transition: all 1s;
}
.dui .line {
border-top: 1px solid red;
width: 100px;
transform: translate(110px, -18px);
transition: all 1s;
opacity: 0;
}
.dui .code-addr {
opacity: 0;
transition: all 1s;
}
.dui.dui-2 {
left: 600px;
transition: all 1s;
}
.undefined,
.b-value {
color: red;
opacity: 0;
transition: all 1s;
}
.opacity-anim {
opacity: 1 !important;
}
.step-anim {
transform: translateY(35px);
}
.line-width {
width: 150px !important;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zxuoo0Vr-1652838732630)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8f0918ba520746e2a21b561f487b8293~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image?)]
.b-value {
color: red;
opacity: 0;
transition: all 1s;
}
.opacity-anim {
opacity: 1 !important;
}
.step-anim {
transform: translateY(35px);
}
.line-width {
width: 150px !important;
}