1.变量提升_充分了解

1.背景

当浏览器开辟出供代码执行的栈内存后,代码并没有自上而下立即执行,而是继续做了一些事情:把当前作用域中所有带var/function关键字的进行提前的声明和定义 =>变量提升机制

  • 带var的只是提前声明(declare),并未提前赋值 ; var a;
  • 带function的既声明,还赋值,准确来说就是让变量和某个值进行关联;
console.log(a);  // undefined;

var a = 12;
var b = a;
b = 13;
console.log(a);  // 12

console.log(sum(1,2));  // 3
function sum(n,m){
     return n+m;
};

/*函数表达式方式,由于使用VAR来创建SUM,变量提升阶段只会声明变量,不会赋值,所以此时函数在前面执行,函数是没有值的,不能执行(真实项目中这种方式最常用,因为它操作严谨)*/

console.log(sum); //=>undefined
// sum(10, 20); //=>Uncaught TypeError: sum is not a function
var sum = function (n, m) {
    return n + m;
};
// let sum=(n,m)=>n+m;
console.log(sum(10, 20));

那么接下来我们通过一张图来了解下;


1.变量提升_充分了解_第1张图片
变量提升.png

2.带var和不带var的区别

  • 在全局作用域下的区别
  • 栈内存变量存储空间

不带var
1、相当于给全局window设置了一个属性a;window.a = 13;
2、不是私有变量,会往它的上级作用域查找,看是否为上级的变量,上级没有,继续找,一直找到window,这种机制叫做“作用域链”;

带var的
1、不但在window上增加一个属性,还在全局作用域下声明了一个变量(只有全局作用域有这个特点var b = 14)

2、在私有作用域变量提升阶段,依然属于私有变量,和外界没有任何关系;

console.log(a); //=>Uncaught ReferenceError: a is not defined
a = 13; //=>window.a=13;
console.log(a);

3. let/const 和var 的区别

1、var存在变量提升,let 和 const不存在变量提升机制(创建变量的六种方式中:var/function 有变量提升,而lconst/class/import 都不存在这个机制);

2、var允许重复声明,而let是不允许的 (只用同域重名全部代码不执行,直接报错)

  • 在相同的作用域中(或执行上下文中,如果使用 var/function 关键词声明变量重复声明,是不会有影响的(声明第一次之后,之后在遇到就不会重复声明了);
  • 但是使用 let/const 就不行,浏览器会校验当前作用域中是否已经存在这个变量了,如果已经存在了,则再次基于let等重新声明就会报错;
    【在浏览器开辟栈内存提供代码自上而下执行之前,不仅有变量提升的操作,还有很多其他的操作=>“词法解析”或者“词法检测”;就是检测当前即将要执行的代码是否会出现“语法错误”;如果出现错误,代码将不会再执行(第一行都不会执行)】;

3、let能解决typeof检测是出现的暂时性死区问题
4、基于let/const/class 等创建变量,会把所在的大括(除对象的大括号外)当做一个全新的私有块级作用域

  • 函数执行会产生私有的栈内存(作用域/执行上下文);
  • let等也会产生私有作用域(var不会);

5、带var相当于在window设置一个属性,同时声明一个变量;但使用let/const相当于只声明一个变量,没有给window增加属性(全局下);

4.注意点

1.在当前作用域下(全局/私有/块级作用域),如果创建变量使用的是let/const等,一定不能创建代码前使用这些变量否则会报错; Uncaught ReferenceError: Cannot access 'b' before initialization

//let 不会做变量提升
console.log(1);=>1
console.log(a); //=>Uncaught ReferenceError: Cannot access 'a' before initialization (未捕获的ReferenceError:无法在初始化之前访问'a')在JS中上一行代码报错,下面的代码都不会再去执行了
let a = 12;
a = 13;
console.log(a);
// 如果let 的变量在声明之前使用了,那就会报错,但是不影响之前代码;


let a = 10;
console.log(a);  // => 10
function fn(){
       console.log(a);  //Uncaught ReferenceError: Cannot access 'a' before initialization
        let a = 12;
//词法解析:就算是私有作用域中存在提前使用let变量,也会提前检测出来;
};
fn();


function fn(){
    console.log(a);  //Uncaught ReferenceError: Cannot access 'a' before initialization
}
fn()

let a = 10;
console.log(a);  // 函数是可以访问外界的元素的
  1. 所谓重复是:不管之前通过什么办法,只要当前栈内存中存在这个变量,我们使用let/const等重复生命这个变量就是语法错误;
//只要重复,代码一行都不执行直接报错;
console.log(1); // 不会执行此行代码; 
let a = 13;
var a = 13;  //Uncaught SyntaxError: Identifier 'a' has already been declared
console.log(a);

3、let/const/function 块级作用域

if (1 === 1) {
         var a = 12;
         let b = 10;
         console.log(b);   //  10
     }
console.log(a);   // 12
console.log(b);  // Uncaught ReferenceError: b is not defined at
// let 会形成块级作用域(私有作用域);


let n = 1;
function fn (){
    console.log(n);  // 1
    if(n){
        let n = 3;
        console.log(n)  // 3
    }
    console.log(n);  // 1
};
fn();

4.(1)函数具有特殊性,在老版本浏览器中,确实不论条件是否成立,函数提前声明或者定义,但是新版本浏览器中,为了兼容ES6严谨的语法规范,条件中的函数在变量提升阶段只能提前声明,不能提前定义;
(2)代码执行 : 自执行函数不进行变量提升(没名字);

// fn==> undefined;执行直接报错;
  fn();
  if('fn' in window){
      function fn(){
          console.log('哈哈哈');
      };
  };
  
  
if('fn' in window){
// 但是只要条件成立,进来后的第一件事情是给FN赋值,然后再代码执行; 
    fn();
    function fn(){
       console.log('哈哈哈');  //哈哈哈;
   };
};



f = function () {return true;}  //=>window.f=...
g = function () {return false;}
~function () {
    /*
     * 函数执行会形成一个私有作用域
     *   1.变量提升  function g;
     *   2.代码执行
     */
    if (g() && [] == ![]) { //=>Uncaught TypeError: g is not a function
        f = function () {return false;}
        function g() {return true;}
    }
}();
console.log(f());
console.log(g());
  1. let能解决typeof检测时出现的暂时性死区问题(LET比VAR更严谨)
console.log(a)   // Uncaught ReferenceError: a is not defined

console.log(typeof a)   //undefined
//=>这是浏览器BUG,本应该报错因为没有a(暂时性死区);

// let可以解决暂时性死区(还没有定义就去使用就会报错);
console.log(typeof a); //=>Uncaught ReferenceError: Cannot access 'a' before initialization
let a;

你可能感兴趣的:(1.变量提升_充分了解)