变量提升与函数提升;var、let、const比较

1、var、const、let
  1. var 声明的变量属于函数作用域,le声明的变量属于块级作用域;但是块级作用域是函数作用域的子集,因此适用于var的作用域限制也适用于let;
  2. var 存在变量提升现象,而 let 和 const 没有此类现象;
  3. var 变量可以重复声明,而在同一个块级作用域,let 变量不能重新声明,const 变量不能修改。
  • var和let声明

var命令会发生”变量提升“现象,即变量可以在声明之前使用,值为undefined。

如果使用关键字 var 声明一个变量,那么这个变量就属于当前的函数作用域,如果声明是发生在任何函数外的顶层声明,那么这个变量就属于全局作用域。举例说明:

var a = 1; //此处声明的变量a为全局变量
function foo(){
    
    
   var a = 2;//此处声明的变量a为函数foo的局部变量
   console.log(a);//2
}
foo();
console.log(a);//1如果在声明变量时,省略 var 的话,该变量就会变成全局变量,如全局作用域中存在该变量,就会更新其值。如:
var a = 1; //此处声明的变量a为全局变量
function foo(){
    
    
   a = 2;//此处的变量a也是全局变量
   console.log(a);//2
}
foo();
console.log(a);//2

注意:var 声明的变量存在提升(hoisting)。
提升是指无论 var 出现在一个作用域的哪个位置,这个声明都属于当前的整个作用域,在其中到处都可以访问到。**注意只有变量声明才会提升,对变量赋值并不会提升。**如下例所示:

console.log(a);//undefined
var a = 1;

//等价于
var a;
consloe.log(a);//undefined
a=1;

而如果对未声明过的变量进行操作,就会报错:

console.log(b);//假设b未声明过,Uncaught ReferenceError: b is not defined
a=1;
console.log(a);//1
var a;

在ES6之前,我们都是用var来声明变量,而且JS只有函数作用域和全局作用域,没有块级作用域,所以{}限定不了var声明变量的访问范围。

  • (1)let 声明存在暂时性死区(TDZ)

let声明的变量不会在作用域中被提升;在let声明之前的执行瞬间被称为”暂时性死区“,在此阶段引用任何后面才声明的变量都会抛出。

let a = 1;
console.log(a);//1
console.log(b);//Uncaught ReferenceError: b is not defined
let b = 2;
  • (2)全局声明

使用let在全局作用域中声明的变量不会成为window对象的属性,var声明的变量则会。

var name = 'Matt';
console.log(window.name);//'Matt'
let age = 'Matt';
console.log(window.name);//undefined

但是let声明仍然是在全局作用域中发生的,相应变量会在页面的生命周期内存续。为了避免错误,必须确保页面内不会重复声明同一个变量。

  • (3)for循环中的let声明

用var声明,for循环定义的迭代变量会渗透到循环体外部,而let声明则不会。

for(var i=0;i<5;i++){
    
    
    //循环逻辑
}
console.log(i);//5
for(let i=0;i<5;i++){
    
    
    //循环逻辑
}
console.log(i);//ReferenceError

以下是一个经典的关于 var 和 let 的一个例子:

//在循环退出时,迭代变量保存的是导致循环退出的值。
for (var i = 0; i <10; i++) {
    
     
    console.log(i); //0  1  2  3  4  5  6  7  8 9
    setTimeout(function() {
    
      // 同步注册回调函数到 异步的 宏任务队列。
         console.log(i);        // 执行此代码时,同步代码for循环已经执行完成
      }, 1000);
}
//先输出 0  1  2  3  4  5  6  7  8 9
//最后输出
//10   共10个 
// let声明迭代遍历时,JavaScript引擎会在后台为每个迭代循环声明一个新的得带遍历,每个setTimeout引用的都是不同的变量实例。
for (let i = 0; i < 10; i++) {
    
     
  console.log(i); //0  1  2  3  4  5  6  7  8 9
  setTimeout(function() {
    
    
    console.log(i);    //  i 是循环体内局部作用域,不受外界影响。
  }, 1000);
}
//先输出 0  1  2  3  4  5  6  7  8 9
// 输出结果:
0  1  2  3  4  5  6  7  8 9

var是在全局范围有效,所以执行setTimeout里的函数时,先是在函数内部寻找 index 变量,没有找到,所以去外层找,找到!这时index已经执行完循环,所以值为5;
let则是声明在for循环的内部的,每一次for循环,一个block上下文,每次for循环都建立如下block。

扫描二维码关注公众号,回复: 13135279 查看本文章
{
    
    
   let index = 0;
   setTimeout(function (){
    
    
     console.log(index);
   }, 10)
}

另外,for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。

for (let i = 0; i < 3; i++) {
  let i = 'love';
  console.log(i);
}
// love
// love
// love

上面代码正确运行,输出了 3 次abc。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域.

  • const

const 声明方式,除了具有 let 的上述特点外;还具备一个特点,声明变量时必须同时初始化变量,一旦定义后,就不能修改,即 const 声明的为常量。

例如:

const a = 1;
console.log(a);//1
a = 2;
console.log(a);//Uncaught TypeError: Assignment to constant variable.

但是,并不是说 const 声明的变量其内部内容不可变,如:

const obj = {
    
    a:1,b:2};
console.log(obj.a);//1
obj.a = 3;
console.log(obj.a);//3

所以准确的说,是 const 声明创建一个值的只读引用。但这并不意味着它所持有的值是不可变的,只是变量标识符不能重新分配

2、变量提升

变量提升是因为在 JS 中执行上下文(也称作用域)的工作方式造成的。ES6之前,JavaScript中函数域为最小域范围;for循环、while循环、if语句、switch语句的“{}”不是作用域。

  • 即使我们在定义这个变量或者函数之前调用它,变量或者函数仍然可以工作。

  • 函数和变量相比,会被优先提升。函数提升是整个代码块提升到它所在的作用域的最开始执行。

  • JS 只会提升声明,不会提升其初始化。如果一个变量先被使用再被声明和赋值的话,使用时的值是 undefined。提升是指无论 var 出现在一个作用域的哪个位置,这个声明都属于当前的整个作用域,在其中到处都可以访问到。

  • 好处:

    • 解析和预编译过程中的声明提升可以提高性能,让函数可以在执行时预先为变量分配栈空间。
    • 声明提升还可以提高JS代码的容错性,使一些不规范的代码也可以正常执行。
3、函数提升
  • 在 JavaScript 中,函数有两种方式进行声明,函数声明会被提升,但是函数表达式却不会被提升
//函数声明式
function bar () {
    
    }
//函数字面量式 
var foo = function () {
    
    }
  • 在 JavaScript 中没有块级作用域,所以 var a = 10;会被 JavaScript 分为两步中的 var a; 会被提升到函数作用域中的最顶端,声明了一个局部变量 a,在 foo(…) {} 的函数作用域中,这个重名局部变量 a 会屏蔽全局变量 a,换句话说,在遇到对 a 的赋值声明之前,在 foo(…) {},a 的值都是 undefined!
var a = true;
foo();

function foo() {
    
    
	if(a) {
    
    
		var a = 10;
	}
	console.log(a);
}
//等价于
function foo() {
    
    
	var a;
	if(a) {
    
    
		a = 10;
	}
	console.log(a);
}
var a;
a = true;
foo();
console.log(v1);
var v1 = 100;
function foo() {
    
    
    console.log(v1);
    var v1 = 200;
    console.log(v1);
}
foo();
console.log(v1);
//执行结果
//undefined
//undefined
//200
//100

猜你喜欢

转载自blog.csdn.net/lujiebin/article/details/115440455