变量提升、块级上下文和函数

11 阅读3分钟

变量提升—JS代码执行的预处理机制

在“当前上下文”中,代码执行之前,浏览器首先会把所有带var/function关键字的进行提前声明或者定义。而其它如let/const/import/class声明的变量不存在变量提升

  • var:声明提前
  • function:声明提前,定义(赋值)提前

在全局上下文中,var声明的变量放在GO中,let/const 声明的变量放在VO中

  • 推荐使用函数表达式,确保函数执行只能放在“创建函数”的下面,保证逻辑严谨性
const fn = function fn(){
   console.log(1)
}
fn();
  • 条件判断:在当前上下文中,变量提升阶段,不论条件是否成立,都要进行变量提升
    • var:还是只声明
    • function:判断体中的函数,在变量提升阶段,只声明不赋值「函数是个渣男」
console.log(a);//undefined
if (!('a' in window)) {
  var a = 13;
}
console.log(a);//undefined
//var a,先声明,全局下,所以window.a = undefined
//!('a' in window)是false,所以a不会赋值,所以两个打印都是undefined

示例

console.log(a);//undefined
var a = 12;
console.log(b);//报错Uncaught ReferenceError: b is not defined
let b = 12;
console.log(fn);//function fn(){ console.log(2); }
function fn(){ console.log(1); }
console.log(fn);//function fn(){ console.log(2); }
var fn = 12;
console.log(fn);//12
function fn(){ console.log(2); }
console.log(fn);//12
var a = 1
function test(){
  console.log(a)//1
  a=0////执行时先找局部作用域,找不到a,就去上级作用域找,找到了a,修改。所以此处修改的是上级作用域(全局作用域)中的a
  console.log(a)//0
}

test()
console.log(a)//0
============================
var a = 1
function test(a){
  console.log(a)//1
  a=0//执行时先找局部作用域,找到了a,就修改。所以此处修改的是局部作用域中的a,上级作用域(全局作用域)中的a不会被修改
  console.log(a)//0
}

test(a)
console.log(a)//1

块级上下文和函数

除“函数和对象”的大括号外「例如:判断体/循环体/代码块…」,如果在大括号中出现了 let/const/function/class 等关键词声明变量,则当前大括号会产生一个“块级私有上下文”;

它的上级上下文是所处的环境;

var不产生块级上下文,也不受块级上下文的影响;

//忽略报错的影响
console.log(a);
console.log(b);
var a = 12;
let b = 13;
if (1 == 1) {
  //产生一个块级上下文【前提是在支持ES6语法规范的浏览器中,才会产生块级上下文(ES6语法才出现)】
    console.log(a);
    console.log(b);
    var a = 100;
    let b = 200;
    console.log(a);
    console.log(b);
}
console.log(a);
console.log(b);

函数是个渣男

存在判断条件的函数,

旧版浏览器:函数在全局上下文中会声明和定义

新版浏览器:为了向后兼容ES5,向前兼容ES6,对函数处理的机制做了一些改变。

@1 变量提升阶段,如果function出现在“除函数和对象”的大括号之内,此时不再是声明+定义函数,而是只声明,不赋值了。

@2 因为“function(){}”既被全局声明过,也被块级声明+定义过,所以执行到这一行代码的时候,会把块上下文中,当前这行代码 以前对foo所有的操作 ,同步给全局。但是之后再对foo的操作,只和私有块级上下文有关系,和全局没关系了。【函数是渣男,一等公民】

console.log(foo);
if (1 === 1) {
    console.log(foo);
    function foo() {}
    foo = 1;
    console.log(foo);
}
console.log(foo);

  • 循环中的块级上下文
 /* EC(G)
   VO(G) / GO
      f -> 0x000 [[scope]]:EC(G)  “return true”
      g -> 0x001 [[scope]]:EC(G)  “return false”
   变量提升:- -
 */
f = function () {return true;};
g = function () {return false;};
(function () {
    /!*
     EC(AN) 
       AO(AN)
          g
       作用域链:<EC(AN),EC(G)>
       初始THIS:window / undefined
       初始ARG:...
       形参赋值:--
       变量提升:function g;  只声明不定义了「因为其出现在判断体中」
     *!/
    if (g() && [] == ![]) {  //Uncaught TypeError: g is not a function
        f = function () {return false;}
        function g() {return true;}
    }
})();
console.log(f());
console.log(g());