基础篇 | 变量 var-let-const

在 ES6 出现之前,js 定义变量的关键字只有 var。 在 ES6 之后才新增了 let 和 const。

新的变量声明关键字出现,也带来了一些新的变化。Enjoy it :)

var

在 ES6 出现以前, JavaScript 定义变量的关键字只有 var

声明作用域

使用 var 操作符定义的变量,会成为包含它的函数的局部变量,这意味着该变量会在函数退出的时候被销毁。

functionfoo() {varname ='foo';console.log(name);// foo}foo();console.log(name);// Error

全局变量

如果在定义变量时,没有使用 var 关键字,这时候,所声明的变量将会变成全局变量

functionfoo() {// without keyword 'var'name ='foo';console.log(name);// foo}foo();console.log(name);// foo

上述代码将会变成下述代码,变量 name 的声明被提到了全局顶部。虽然可以通过这种方式来声明一个全局变量,这种方式强烈不推荐,这会使得全局变量被污染,也会使得代码变得难以维护。

varname;functionfoo() {  name ='foo';}

在严格模式下,如果未声明直接进行赋值操作,会直接抛出 ReferenceError 异常。

提升 Hoisting

在开始这小节之前,先思考一下下述代码会打印什么内容呢? 会直接报错抛出 Error 么? 还是正常打印出一个 foo? 或者是 undefined?

functionfoo() {console.log(name);varname ='foo';}

在解答之前,首先明确,声明和赋值其实是两个动作。 当我们用 var name = 'foo', 其实这里面包含着两个动作:声明和赋值。首先是声明 var name, 声明了一个 name 的变量,其次是赋值,将 name 赋值为 foo。

现在开始回答,上述代码不会报错。当使用 var 进行变量声明时,这时变量的声明会自动提升到函数作用域的顶部,然后执行打印,这时候,name 还是一个只声明为赋值的变量,所以这时候打印 name 会打印出 undefined, 之后才执行变量的赋值操作。听起来云里雾里,也就是,上面的代码实际执行的顺序如下:

functionfoo() {varname;console.log(name);// undefinedname ='foo';}

所谓的变量提升指的就是将所有变量声明提升到函数作用域顶部,如果声明多次,只要在同一个作用域内,重复声明也没有关系。但是注意,赋值依旧会停留在原地。提升只针对声明而言,赋值依旧是按照原来的顺序进行赋值操作。

functionfoo() {console.log(name);// undefinedvarname ='foo';varname ='bar';console.log(name);// barvarname ='ba1r';console.log(name);// ba1rvarname ='bar2';}

let & const

let 和 const 是自 ES6 发布以来新增的变量定义关键字,和 var 最大的差异是,新增的 let 和 const 是块级作用域,而非是函数作用域。

块级作用域

let 和 const 是块级作用域。JavaScript 引擎会记录用于变量声明的标识符及其块作用域。块级作用域顾名思义,它的作用域仅限于该块的内部

if(true) {letage =30;console.log(age);// 30}console.log(age);// ReferenceError: age is not defined

Quick question: 下面的执行结果是什么呢? 可以自己动手试试看。

{leta =42;}console.log(a);// ?

No hoisting & TDZ 暂时性死区

经由 var 声明的变量是可以重复进行声明,得益于 hoisting, 所有的变量声明是会提升到作用域的顶部,所以即使重复声明也是没有问题的。和 var 不同, let 和 const 的变量声明是不会在作用域中提升的。在解析代码的时候,JavaScript 引擎会注意到块作用域中,会存在一个 let的声明,在执行到对应语句之前,不能用任何方式来引用未声明的变量。由此引出了一个 暂时性死区 temporal dead zone 的概念。

TDZ (temporal dead zone)

TDZ 是 ES6 规范中定义的一个新的概念,指的是由于代码中的变量尚未进行初始化而无法被引用的情况。 在执行到对应语句之前,无法用任何方式来引用尚未声明的变量。因此,在块级作用域执行到对应 let的声明之前,称之为暂时性死区 (temporal dead zone, TDZ)。 如下文的代码,在执行到实际声明 age 之前,如果引用到 age 这个变量,会抛出 ReferenceError。

if(true) {console.log(age);// ReferenceError: Cannot access 'age' before initializationletage =30; }

同样的, 在函数参数再传参过程中也有所谓的“死区”,举个例子🌰

functionbar(x = y, y =2) { ... }

这个时候, y 尚未被声明,所以这样的参数声明会报错。而,下面这种情况,调换了 x 和 y 的顺序,在访问 y 时,刚好跨出了 x 的 TDZ, 所以y的这种访问方式则没有问题。

functionbar(x =2, y = x) { ... }

由于 const 变量要求声明时立刻进行初始化,则不存在 TDZ 的问题。

全局声明

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

但是相同的是,在全局作用域发生的声明,会在生命周期内续存。因此,let 必须保证不会重复声明同一变量。

typeof & try...catch 的陷阱

划重点!let 和 const 是块级作用域。 在下述的例子中,let 的声明都会被限制在对应的块级作用域之中,所以无法实现对应的条件声明的效果。

if(typeofname ==='undefined') {letname;// name 只存在于 if{...} 之中}

try{console.log(age);}catch{letage =25;console.log(age);// age 只存在于 catch 之中}

循环利器

我们经常会看到一道面试题,请问到底会打印出什么内容。

for(vari =0; i <5; i++) {setTimeout(() =>{console.log(i);// 5, 5, 5, 5, 5},200);}

这是因为,使用了定时器,打印被延迟执行。而 var 声明的变量是函数作用域, i 已经走到了 5。 当定时器实际 log 的时候,其实循环已经执行完毕了,所以打印出来了 5 个 5。

如果我们更换成了 let 来声明迭代变量,则能够打印出 0,1,2,3,4。 这是因为,改用了 let 之后,每次迭代作用域被限制在了 for 循环块内部。 JavaScript 引擎会为每个迭代循环声明一个新的迭代变量,因此,每个定时器引用的都是不同的变量实例,也就是循环执行过程中每个迭代变量的值。

for(leti =0; i <5; i++) {setTimeout(() =>{console.log(i);// 0, 1, 2, 3, 4},200);}

What about const

前面经常把 let 和 const 并列起来讲。但是,let 和 const 之间也是有些许的差异。主要的差异点可以从 const 的这个单词看出来, const means constant, 意为常量,使用const 声明的变量存在不可变性,变成一个“常量”。https://b23.tv/5CVC7NX

https://b23.tv/BY7NBFf

https://b23.tv/0eHr98g

https://b23.tv/qVGZIUB

let 和 const 的行为最大的差异,在于 const 的声明和赋值操作必须同时,尝试修改 const 声明的变量会导致运行时出错。

但是,const 声明的限制只适用于它所指向的变量的引用。换言之,如果 const 变量引用了一个对象,那么修改这个对象内部的属性值并不违反 const 的限制。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容