Open yoowinsu opened 7 years ago
本文首发在个人博客yoowin.me
看了方应杭老师的一篇解释let的文章,对JavaScript中的声明有了深刻的理解,这里也就有了总结一下JavaScript中各种声明之间区别的这篇文章。
首先,我对JavaScript中所有声明都存在提升这个观点是认同的!
JavaScript中所有声明都存在提升
平时大家所讲的变量的声明,在JavaScript中一般是存在创建create、初始化initialize 和赋值assign三个过程的,其中函数声明也是一样有这三个过程的。在这三个过程中:
声明
创建create
初始化initialize
赋值assign
创建:即在变量所在作用于头部抛出变量,仅仅是抛出,是不能被使用的; 初始化:在变量初始化之前变量是不能被使用的,初始化只有一次,初始化之后变量可以使用; 赋值:即覆盖初始化的值
在let、var、const(包括函数声明)的声明过程中,他们的创建create、初始化initialize 和赋值assign是有区别的。
let是ECMAScript2015(ES6)中新增的变量声明方式,let语句可以声明一个块级作用域的变量。
在同一个函数或同一个作用域中用let重复定义一个变量将引起 TypeError
if (true) { let foo; let foo; // TypeError thrown. }
在let语句中,创建create、初始化initialize 和赋值assign的过程如下:
if (true) { let foo console.log(foo) //undefined foo = 'str' console.log(foo) //"str" }
通过上面代码可以看出let语句是如下过程的:
第一步:在块级作用域中,找到let语句并声明提升,创建foo变量,但是在初始化之前使用是会报错的(如下面代码); 第二步:执行let foo,let语句使得foo变量被初始化,初始化的过程是可选的,可以let foo初始化foo为undefined,也可以let foo = 123初始化foo为123.初始化之后变量可以使用; 第三步:执行foo = 'str',foo变量可以被赋值语句覆盖
let foo
let foo = 123
foo = 'str'
if (true) { console.log(foo) //Uncaught ReferenceError: foo is not defined let foo; }
所以说,let声明中在初始化之前是存在“暂时性死区”的。
还有值得注意的是,初始化只能一次,如果变量初始化失败,则会存在一个该变量既不能赋值又不能使用的BUG,如下图:
因为let foo = foo导致初始化失败,所以导致了foo所在作用域都是foo变量只创建而未被初始化,所以foo所在作用域都会是foo变量的“暂时性死区”
let foo = foo
var声明是函数作用域。 众所周知,var声明是有变量提升的,有了上文的let声明的理解,也就对var理解起来更加轻松了。
(function() { console.log(f) //undefined var f = 'str' // console.log(f) //"str" }())
var声明的过程:
第一步:在函数作用域中,找到var语句,f变量得到声明提升到作用域顶部,创建f变量并初始化为undefined,在赋值之前f的值就是undefined; 第二步:执行f = 'str',f变量被赋值为'str'
f = 'str'
所以,在执行var f = 'str'之前,打印的f的值为undefined
var f = 'str'
const声明创建了一个常量 const和let比较像,也是块级作用域,区别就是,const在定义的时候必须初始化,而且不能被赋值
如上图,const在定义时必须初始化,否则会报错Uncaught SyntaxError: Missing initializer in const declaration;const是常量,声明之后不能再次赋值,否则会报错Uncaught TypeError: Assignment to constant variable
Uncaught SyntaxError: Missing initializer in const declaration
Uncaught TypeError: Assignment to constant variable
函数声明在JavaScript存在变量提升,这也被众多开发者做开发所常用。
fn() function fn(){ console.log(this) }
函数声明的过程:
第一步:在作用域中,找到function语句,将fn函数声明提升到作用域顶部,创建fn函数,初始化并赋值为function fn(){console.log(this)}; 第二步:执行fn()
function fn(){console.log(this)}
fn()
即函数声明是在找到function语句后,作用域顶部创建、初始化和赋值一步到位的
在MDN关于let讲解中,说let没有声明,也许是考虑到更容易被开发者接受和理解吧,但是我认为JavaScript中所有声明都存在提升是正确的。
最后用一个表格总结一下let、var、const声明的区别吧
前言
看了方应杭老师的一篇解释let的文章,对JavaScript中的声明有了深刻的理解,这里也就有了总结一下JavaScript中各种声明之间区别的这篇文章。
JavaScript中变量声明机制
首先,我对
JavaScript中所有声明都存在提升
这个观点是认同的!平时大家所讲的变量的
声明
,在JavaScript中一般是存在创建create
、初始化initialize
和赋值assign
三个过程的,其中函数声明也是一样有这三个过程的。在这三个过程中:在let、var、const(包括函数声明)的声明过程中,他们的
创建create
、初始化initialize
和赋值assign
是有区别的。let
let是ECMAScript2015(ES6)中新增的变量声明方式,let语句可以声明一个块级作用域的变量。
在同一个函数或同一个作用域中用let重复定义一个变量将引起 TypeError
在let语句中,
创建create
、初始化initialize
和赋值assign
的过程如下:通过上面代码可以看出let语句是如下过程的:
所以说,let声明中在初始化之前是存在“暂时性死区”的。
还有值得注意的是,初始化只能一次,如果变量初始化失败,则会存在一个该变量既不能赋值又不能使用的BUG,如下图:
因为
let foo = foo
导致初始化失败,所以导致了foo所在作用域都是foo变量只创建而未被初始化,所以foo所在作用域都会是foo变量的“暂时性死区”var
var声明是函数作用域。 众所周知,var声明是有变量提升的,有了上文的let声明的理解,也就对var理解起来更加轻松了。
var声明的过程:
所以,在执行
var f = 'str'
之前,打印的f的值为undefinedconst
const声明创建了一个常量 const和let比较像,也是块级作用域,区别就是,const在定义的时候必须初始化,而且不能被赋值
如上图,const在定义时必须初始化,否则会报错
Uncaught SyntaxError: Missing initializer in const declaration
;const是常量,声明之后不能再次赋值,否则会报错Uncaught TypeError: Assignment to constant variable
函数声明
函数声明在JavaScript存在变量提升,这也被众多开发者做开发所常用。
函数声明的过程:
即函数声明是在找到function语句后,作用域顶部创建、初始化和赋值一步到位的
总结
在MDN关于let讲解中,说let没有声明,也许是考虑到更容易被开发者接受和理解吧,但是我认为
JavaScript中所有声明都存在提升
是正确的。最后用一个表格总结一下let、var、const声明的区别吧