函数提升优于变量提升?

同样是提升 ,那么函数提升在前还是变量提升在前,又或者是按照代码顺序提升?
你不知道的JavaScript上卷中第40页提到 函数声明是优于变量提升的,不过我个人还有另一种看法。
 
书上说的看完了,接下来看我个人的说法。
我个人的看法就是:实际上是提升没有谁优先,而是按顺序来的。首先要了解的就是变量实际上是内存的一块区域,里面是存的值(引用数据类型是存的地址指针),还有就是JavaScript引擎很懒,不会对已经声明的变量再次声明,但是会进行赋值覆盖(这是公认的,实际上引擎怎么去做的,没有人知道)。
来看一段代码
fn();// 'b'

function fn(){
    console.log('a')
}

var fn;

function fn(){
    console.log('b')
}
fn();
// 'b'

//这里最终两次都是输出b 我个人猜测JavaScript引擎实际上是这么做的
/*
  全局作用域预编译环境  内存先开辟一块栈空间(这时候开辟的是全局作用域栈)  用来存放全局作用域的变量  内存空间分为栈和堆两种  下面有解释
*/  
 
/*  
  第一步:
   预编译环境妹妹和栈哥哥(全局作用域,函数作用域栈的问题不在这里讨论,下面说的栈都是全局作用域栈)以及堆哥哥的对话
    妹妹: 栈哥哥,你有没有一块名字叫fn的空间
    栈哥哥: 我现在没有
    妹妹: 哦,那你帮我开辟一块好不好,我现在需要
    栈哥哥: 好的,现在有了
  预编译环境妹妹:谢谢栈哥哥,你帮我把
    function (){
         console.log('a')
      }
    存到那里面去吧
   栈哥哥:好的,不过你这个是引用类型哦,我帮你存到我兄弟堆空间里面去吧,等会把堆的地址给你,你要用的话就拿这个地址去找它就好了(解释一下,内存分两种,一种是栈空间(作用域,栈先进后出,全局作用域最先被声明,但是最后被销毁,它要等到该作用域里所有变量的引用都消失的时候才会销毁,这也就是为什么滥用闭包会造成内存泄露的原因,一旦一个变量一直被引用,那么包含它的所有作用域都不会被销毁),用来存值类型,一种是堆空间,用来存引用数据类型,如object(普通对象、数组、日期对象、正则对象等等)、function,栈空间里存了堆空间的引用地址(指针),可以根据这个地址找到堆空间里你要的函数或者对象)。然后对堆说:堆兄弟,帮我开辟一块空间,把这东西放进去,然后把地址给我
  堆哥哥: 栈兄弟,我存好了,地址给你。反手扔过去一个存函数的地址。
  栈哥哥: 妹妹,地址给你,存好别丢了哦,要的话找我就行。
  预编译环境妹妹: 好的,谢谢栈哥哥,么么哒。(堆内心OS:我呢?怎么不谢谢我?哎,以后请叫我雷锋。)
   */
    function fn(){
       console.log('a')
    }
 
  /*
    第二步:
      预编译环境妹妹: 栈哥哥,又要来麻烦你了,你有没有一块名字fn的空间
     内存哥哥: 你刚才问过了,现在有
     预编译环境妹妹: 不好意思啊,我有间断性失忆,那没事了(js引擎不会对已经声明的变量重复声明,会忽略后面的声明,只有第一次声明有效)
  */
  var fn;
 
  /*
    第三步:
      预编译环境妹妹: 栈哥哥,又要来麻烦你了,你有没有一块名字fn的空间
     内存哥哥: 你问过两遍了,这是第三遍了,我说了,现在有
     预编译环境妹妹: 问过了吗,我忘了,我有间断性失忆,不好意思啊,啊,对了麻烦你帮我把
      function (){
           console.log('b')
       }
      存到里面去
     内存哥哥:好的,我帮你存好了(雷锋,帮我存一下这个),地址给你(这里有两种可能,同样是引用类型赋值,一种可能是把之前的堆空间销毁,重新开辟一个空间储存,把新的地址存到栈里,第二种可能就是直接通过地址找到那个堆空间,然后把里面的干掉,然后鸠占鹊巢,我比较倾向于第二种情况)
    预编译环境妹妹: 好的,谢谢哥哥,你忙吧,不打扰你了,么么哒(雷锋:又没我啥事?)
  */

   function fn(){
     console.log('b')
   }
//执行代码的时候
/*
全局执行环境妹妹和栈哥哥,堆哥哥的对话
妹妹: 栈哥哥,你有没有fn
栈哥哥: 有,给你(栈哥哥发现是一个地址后就拿这个地址去找堆兄弟。雷锋,帮我拿一下这个,小妹要用。雷锋:给你赶紧滚,见色忘义的家伙,也不知道提一下我的存在),你用完了和我说一下
妹妹: 好的,谢谢哥哥,我要干活了(拿到的那个函数是执行console.log('b'),调用这个函数的时候会生成一个函数作用域,也是先进行变量和函数的提升,在内存里开辟一块栈空间用来储存这个函数作用域里的变量,当执行完函数之后,如果这个作用域里的变量没有被引用的,那么会告知内存,销毁这个作用域栈空间,函数每次调用的时候都会生成一个作用域,也就是会新开辟一块栈空间)
*/
fn();
/*
妹妹: 栈哥哥,又来找你了,
你有没有fn
栈哥哥: 给你(同上)
妹妹: 我执行完了,没有引用的变量了
全局作用域栈这个时候感觉到fn已经不被需要了,同时自己也不被需要了,身体已经被掏空了,于是把fn空间(只是栈里面的)销毁后进行了自我销毁(GC回收)
堆这个时候感觉到那个函数已经不被需要了,于是把那块储存那块函数的内存释放了(GC回收) (销毁和释放只是说法不同而已,相当于同义词)
*/
fn();
 

猜你喜欢

转载自www.cnblogs.com/wushang941018/p/9416821.html
今日推荐