20s动画带你进入js运行的底层世界

面试题

曾经有这样一道面试题:在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?)]

step.png

.b-value {
color: red;
opacity: 0;
transition: all 1s;
}

.opacity-anim {
opacity: 1 !important;
}

.step-anim {
transform: translateY(35px);
}

.line-width {
width: 150px !important;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值