美文网首页
触及知识盲区的一道题

触及知识盲区的一道题

作者: 阿羡吖 | 来源:发表于2020-11-26 14:26 被阅读0次
image.png

如图所示,粗略看一下 应该输出都是21。
其实,正确答案是内部:21,外部:1。
在JS中没有块级作用域,为什么if就会影响最终的结果呢。

本文要点

1、神奇的let
2、函数块级作用域

什么是变量提升(Hosting)

主要分为变量提升和函数提升

变量提升

变量的提升是以变量作用域来决定的,即全局作用域中声明的变量会提升至全局最顶层,函数内部声明的变量只会提升到该函数作用域的最顶层。

入门级
console.log(a);
var a = 0;

这个并不会报:Uncaught ReferenceError: a is not defined。
而是输出:undefined
因为变量提示后的结果是:

  var a;
  console.log(a);
  a = 0;
进阶级
var x =0;
function a(){
  console.log(x);
  let x = 1;
}
a();

如果let x不存在变量提升,那应该输出0。实际上:


image.png

它不是报错x not defined 而是Cannot access。
例子2:

let a =a;
let a  = a;
image.png

这里看起来 好像let 也会"变量提升",如果不提升的话 例子1的x应该输出0,例子2应该报错a not defined。
但是如果变量提升,貌似也不对啊。那上面的例子1应该输错undefined。
我们管着这叫"暂时性死区"。
实际上这个既不是理解的变量提升,也不是没有变量提升,那什么是暂时性死区呢?
let定义变量是有一个"特殊声明"的过程,JS预解析的时候,先将定的 let ,const "特殊声明"提前,类似"举手",JS引擎规定了同一个作用域,同一个变量只能被一次"举手"。
这里不同于vue的定义和赋值,var的声明式如果已经声明了,后者直接忽略声明。

回看例子2:

let a =a;  //定义了变量a,我暂时标识为a1
let a  = a; // 定义了变量a,我暂时标记为a2

预解析,将a1声明,然后准备将a2声明,这时,JS引擎发现,声明a2的时候,已经有a1声明了。
于是违反了"同一个作用域,同一个变量只能被声明一次"的规定,直接报错。实际上代码中赋值的a变量还没读取。(在读取变量的时候才可能抛变量未定义的错误)。
所以,报错。错误提示:a2已经被声明了(被a1声明了a)。
所以,上述例子1,代码在读取x的时候,发现已经let声明的x,但是并未初始化,才直接报错x无法访问。
那么let变量"特殊声明"是一个什么样神奇的东西呢。
实际上JS引擎是为了解决这个let变量提升时引入的 declareData,在预解析的时候,里面存储了作用域里面所有的let 和 const声明的数据。
事实上,作用域内所有的函数和变量的创建都需要校验是否与declareData的值冲突。
例子3:

var a = 1; // 定义了变量a,暂时标记为a1
let  a =2;  //定义了变量 a,暂时标记为a2

declareData 声明变量a2,然后准备定义变量a1,发现declareData已经有声明a2了,直接报错,a1被声明了,因为已经由a2声明了变量a。

函数提升

函数提升,类似于变量提升,但是确有些许不同。

函数表达式
console.log(a); //undefined
var a = function(){}

console.log(a);   // function a
function a(){};

函数会声明提升,函数表达式不会声明提升,第一个例子输出的是undefined。而不是not undefined。是因为中了var a的变量提升。

块级作用域
console.log(a);   // undefined
if(true){
  console.log(a);  // function a
  function a();
}

如果是变量提升,是不存在变量作用域的,但是函数声明提升存在的。这个预解析如下:

var a;  // 函数a的声明。
console.log(); //undefined
if(true){
  function a(){} //函数a的定义
  console.log(a)  // function a
}

其实函数function a() 在经过预解析之后,将函数声明提升到函数级作用域最前面,然后将函数定义提升到块级作用域最前面。
注意:这里的函数定义是提升到块级作用域最前面。

再看原题
var a;  // 函数a的声明提前
var a = 0; // 已经声明了 a,这里会忽略声明,直接赋值为0
if(true){
  function a(){}  // 函数定义a声明提升到块级最前面
  a = 1; //这里将块级作用域最前面的函数a 重置为1了。
  a = 21;
  console.log("里面",a);
  console.log("外面",a);
}

综合上述,首先 if里面的function a(){}会声明提升,将声明"var a"移到函数级作用域最前面,将函数定义移到块级作用域最前面。
这里有一个问题、函数本身是【定义函数名变量 指针方向 函数内存块】。
函数提升是在块级作用域,但是函数名变量是函数级别的作用域。所以在块级的函数定义(原始代码函数的声明位置)的时候,会将函数名变量同步到函数级作用域,实际上,只有这个时候,在块级作用域外才能访问到函数定义。

预解析如下:
 var a = 0;
if(true){
  console.log(a,window.a); //函数提升 是块级作用域 输出function a 和 0;
  a = 1; // 取作用域最近的块级作用域的function a ,且 被重置为1 本质又是一个 变量的赋值。
  console.log(a,window.a) // a是指向块级作用域的a 输出1 和 0;
  function a(){} // 函数的声明 将执行函数的变量的定义同步到函数级的作用域。
  console.log(a,window.a); //输出1 和1;
  a = 21; //仍然是函数定义块级作用域的a 重置为21
  console.log("里面",a);
}
 console.log("外面",a)

本文转自:https://mp.weixin.qq.com/s/YQEBZo1pdy-5B1Jz8cvWmw

相关文章

  • 触及知识盲区的一道题

    如图所示,粗略看一下 应该输出都是21。其实,正确答案是内部:21,外部:1。在JS中没有块级作用域,为什么if就...

  • js逗号操作符

    最近看了些其他人分享的面试经历 发现了这么一道题 突然就陷入了沉思,这好像触及到了我的知识盲区... 一开始以为这...

  • ciscn-2018-pwn-wp

    echo_back 这题看了大佬的wp才写出来的,触及到了我的知识盲区。。。 参考链接 : http://p4nd...

  • 育儿丨在与女儿的画画PK中完败

    01 昨晚,女儿突发奇想,想要跟我PK画画。听她冒出这个点子,我当下心里一凉:完了,触及到我的知识盲区了。 不仅画...

  • 计算机网络----http缓存控制知识整理

    http缓存机制在使用nginx设定静态资源是否被缓存时就会被用到,当初做项目时就遇到这个触及我知识盲区的问题。所...

  • 二百零六页,自己是自己的屏障

    我们和别人打交道的时候,很容易触及到自己以为别人不知道的知识盲区,这就是自己的屏障,因此,很有可能我们自己才是小丑...

  • 她明明只生了两个孩子,还有四个是复制粘贴……

    许大叉:现代医术??这触及了我的知识盲区 张小狗:…… 张小狗:还不如说这是从魔法世界来的靠谱 我:嗐…那还怀啥,...

  • 知识盲区

    有的孩子天生爱读书,有的孩子需要后天的引导。有时候不是孩子不够优秀,是我们自己不够优秀,最羡慕那些博览群书,博古通...

  • 知识盲区

    群聊里的消息让人第一次感受到了来自知识盲区的巨大力量。 桥水是个啥,对冲基金是个啥。 ...

  • 知识盲区

    活到老,学到老,诚不我欺。长期混吃等死不思进取的我今天栽了个大跟头,一张老脸都在小家伙们面前丢尽了。 上午,根据医...

网友评论

      本文标题:触及知识盲区的一道题

      本文链接:https://www.haomeiwen.com/subject/fdqoiktx.html