zoukankan      html  css  js  c++  java
  • JS变量提升机制

    变量提升机制

    变量提升

    当栈内存(作用域)形成,JS代码自上而下执行之前,浏览器首先会把所有带“VAR/FUNCTION”关键字的进行提前的“声明”或者“定义”,这种预先处理机制称之为“变量提升”

    • 声明: var a (默认undefined)
    • 定义: a = 12(定义其实就是赋值操作)

    变量提升阶段

    • 带“VAR”的只声明未赋值
    • 带“FUNCTION”的声明和赋值都完成了

    变量提升只发生在当前作用域(例如:开始加载页面的时候只对全局作用域下的进行提升,因此此时函数中存储的都是字符串而已)在全局作用域下声明的函数或者变量是“全局变量”,同理在私有作用域下声明的变量是“私有变量”【带VAR/FUNCTION的才是声明】

    浏览器很懒,做过的事情不会重复执行第二遍,也就是,当代码执行遇到创建函数这部分代码后,直接跳过即可(因为在提升阶段就已经完成函数的赋值操作了)

    私有作用域形成后,也不是立即执行代码,而是先进行变量提升(变量提升前,先形参赋值)

    在ES3/ES5语法规范中,只有全局作用域和函数执行的私有作用域(栈内存),其他不生成栈内存

    console.log(a);
    var a = 12;
    b = 13;
    function sum(){
        var total = null;
    }
    sum()
    

    ->undefined

    带VAR和不带VAR的区别

    在全局作用域下声明一个变量,也相当于给WINDOW全局对象设置了一个属性,变量的值就是属性值(私有作用域中的声明的私有变量和WINDOW没啥关系)

    console.log(a);
    console.log(window.a)
    console.log('a' in window);
    

    -> undefined
    -> undefined
    -> undefined

    全局变量和window中的属性存在“映射机制”一个改变另一个也改变

    var a = 12;
    console.log(a);
    console.log(window.a);
    a = 13;
    console.log(window.a);
    window.a = 14;
    console.log(a);
    

    -> 12
    -> 12
    -> 13
    -> 14

    可以使用属性名 in 对象,检测属性名是否隶属于这个对象

     console.log(a);
     console.log(window.a);
     console.log('a' in window);
     a = 12;
     console.log(a)
     console.log(window.a)
    

    -> a is not defined
    -> undefined
    -> false
    -> 12
    -> 12

    全局变量和WINDOW中的属性存在“映射机制”一个改变另一个也改变

    console.log(a);
    console.log(window.a);
    console.log('a' in window);
    a = 12;
    consoel.log(a);
    console.log(window.a);
    
    

    -> 报错:a is not defined
    注释掉报错的第一行
    -> undefined
    ->false
    -> 12
    -> 12
    -> 12

    连续赋值运算下的VAR

    var a = 12, b = 13;
    

    这样写是带VAR的

    var a = b = 12;
    

    这样写b是不带VAR的

    私有作用域域中带VAR和不带也有区别

    • 带VAR的在私有作用域变量提升阶段,都声明未私有变量,和外界没有任何的关系
    • 不带VAR不是私有变量,会向它的上级作用域查找,看是否为上级的变量,不是,继续向上查找,一直找到WINDOW为止(这种查找叫做:“作用域链”),也就是我们在私有作用域中操作的非私有变量,是一直操作别人的。
    var a = 12, b = 12;
    function fn(){
        console.log(a, b);
        var a = b = 13;
        console.log(a,b);
    }
    fn();
    console.log(a,b);
    

    -> undefined 12
    -> 13 , 13
    -> 12 ,13

    变量提升阶段, 私有变量a被提升,声明a为私有变量并赋值为undefined
    var a = b = 13;
    把私有的A赋值为13,通过作用域链查找B并赋值为13

    函数的提升

    • FUNCTION:声明的函数在变量提升阶段被声明
    • 匿名函数之函数表达式: var fn = function(){...},左边被提升,未执行到该行时,fn为undefined

    在条件的变量提升

    在当前作用域下,不管条件是否成立都要进行变量提升

    • 带VAR的还只是声明
    • 带FUNCTION的在老版本浏览器渲染机制下,声明+定义都处理,但为了迎合ES6中的块级作用域,新版浏览器对于在条件判断中的函数,不管条件是否成立,都只是先声明,没有定义,类似VAR

    重名处理

    带VAR和FUNCTION关键字声明相同的名字,这种也算是重名了(其实是一个FN,只是存储值的类型不一样)

    var fn = 12;
    function fn(){};
    

    关于重命名的处理:
    如果名字重复了,不会重新声明,但是会重新定义(重新赋值)(不管是变量提升还是代码执行阶段皆是如此)

    LET

    LET创建的变量不存在变量提升问题,且不会与window产生映射

    在相同作用域中,基于LET不能声明相同名字的变量(不管用什么方式在当前作用于下声明了变量,再次使用LET创建变量都会)

    虽然没有变量提升机制,但是在当前作用域代码自上而下执行之前,浏览器会做一个重复性检测:

    自上而下查找当前作用域下所有变量,一旦发现有重复的,直接抛出异常,代码也不会在执行了

  • 相关阅读:
    秋叶收藏集, LC个人竞赛题目解析
    字典树,前缀树的模板!秒懂
    106. 从中序与后序遍历序列构造二叉树
    c++ enum 的枚举
    c++变量的声明和定义
    leetcode 39 组合总数(回溯)
    python lambda表达式应用
    python解压可迭代对象赋值给多个变量
    python之解压序列并赋值给变量
    Python循环列表的方法
  • 原文地址:https://www.cnblogs.com/xiaoxu-xmy/p/13637135.html
Copyright © 2011-2022 走看看