比较var和let的区别

什么是作用域

块级作用域:即在{}花括号内的域,由{ }包括,比如if{}块、for(){}块。注意函数快也叫做块

函数作用域:变量在声明它们的函数体以及这个函数体嵌套的任意函数体都是有定义的。

JS中作用域有:全局作用域、函数作用域,是没有块作用域的。

块级作用域是ES6中的。

什么是undefined

Undefined类型只有一个值,即undefined。当声明的变量还未被初始化时,变量的默认值为undefined。
Null类型也只有一个值,即null。null用来表示尚未存在的对象,常用来表示函数企图返回一个不存在的对象。

js 代码
 
var oValue;  
alert(oValue == undefined); //output "true"  

这段代码显示为true,代表oVlaue的值即为undefined,因为我们没有初始化它。

js 代码
 
alert(null == document.getElementById('notExistElement'));  

当页面上不存在id为"notExistElement"的DOM节点时,这段代码显示为"true",因为我们尝试获取一个不存在的对象。

js 代码
 
alert(typeof undefined); //output "undefined"  
alert(typeof null); //output "Object"  


第一行代码很容易理解,undefined的类型为Undefined;第二行代码却让人疑惑,为什么null的类型又是Object了呢?其实这是JavaScript最初实现的一个错误,后来被ECMAScript沿用下来。在今天我们可以解释为,null即是一个不存在的对象的占位符,但是在实际编码时还是要注意这一特性。

js 代码
 
alert(null == undefined); //output "true"  

 

ECMAScript认为undefined是从null派生出来的,所以把它们定义为相等的。但是,如果在一些情况下,我们一定要区分这两个值,那应该怎么办呢?可以使用下面的两种方法。

js 代码
 
alert(null === undefined); //output "false"  
alert(typeof null == typeof undefined); //output "false"  

 

下面进入正题

1.直观上

var的作用域要大一些,由于var的作用域是函数级作用域所以var不受块级的约束,对于var声明的变量,只有函数才能为它创建新的作用域.

{
var a = "DOMAIN";
}
console.log(a)//"DOMAIN"

 

有了let之后,才可以像C语言一样,直接就是:

{
let a = "DOMAIN";
}
console.log(a)//Error: a is not defined

2.变量提升

先上一个简单的例子:

console.log(a);//undefined

var a = "hey I am now hoisting";

我们在a被声明前调用a,没有报错,反而是返回一个undefined值,原因是:a其实已经在调用前被声明了,只是没有被初始化。JavaScript会把作用域里的所有变量和函数提到函数的顶部声明,相当于:

var a;
console.log(a);//undefined
a = "hey I am now hoisting";

JavaScript会使用undefined缺省值创建变量a,注意,事实上浏览器并没有把声明语句放到作用域的顶部,在编译阶段,控制流进入域,该域所有的变量和函数的声明先进入内存,文中代码的相对位置不会变动的。

由此可以知道,变量提升指的是变量声明的提升,不会提升变量的初始化和赋值。

3.let变量暂时性死区现象

首先明确一点,变量var与let都存在变量提升现象

let变量必须在声明后使用,否则会报错。console.log(aLet)抛出了一个错误,浏览器认为aLet并没有声明,这是否意味着let并没有使a变量提升呢?

console.log(aVar); // undefined
console.log(aLet); // causes ReferenceError: aLet is not defined
var aVar = 1;
let aLet = 2;

再看一个例子

let a = "hey I am outside";
if(true){
console.log(a);//"hey I am outside"
}

if{}块内打印全局let变量,很正常

let a = "hey I am outside";
if(true){
    console.log(a);//Uncaught ReferenceError: a is not defined
    let a = "hey I am inside";
}

注意看,这里同样抛出了一个错误,认为a没有声明,但是,如果a没有变量提升,执行到console.log时应该是输出全局作用域中的a,而不是出现错误

Temporal Dead Zone (TDZ) 翻译为中文即为 暂时性死区

先看一段 MDN 上关于暂时性死区的定义

let bindings are created at the top of the (block) scope containing the declaration, commonly referred to as “hoisting”. Unlike variables declared with var, which will start with the value undefined, let variables are not initialized until their definition is evaluated. Accessing the variable before the initialization results in a ReferenceError. The variable is in a “temporal dead zone” from the start of the block until the initialization is processed.

文档第一句话就明确指出 let 存在变量提升,但是与 var 不同的是,var 的变量提升的同时会默认赋值为 undefined. 而 let 仅仅发生了提升而已,并不会赋任何值给变量,在显式赋值之前,任何对变量的读写都会导致 ReferenceError 的报错。

最重要的是从代码块(block)起始if(true){位置到变量求值(包括赋值)let a = "hey I am inside";这之间的这块区域,称为该变量的暂时性死区。在死区中,禁止访问该let变量

 

posted @ 2020-03-13 13:17  月半大熊猫  阅读(398)  评论(0编辑  收藏  举报