
变量的声明是每一种编程语言中最基础的部分,在大多数的编程语言中,变量总是在声明的地方创建,然后 JavScript 并不是这样,变量的实际创建位置取决于你如何声明它,加上 es6 引入了 let 和 const 便于在开发中更好的控制变量的作用域。同样这部分知识也是面试中经常遇到的问题。本篇博客就来记录一下关于 JavScript 中变量声明绑定的学习笔记。
var 声明的变量,如果不在函数内部,则视为全局作用域顶部声明的变量,若在函数内部,视为当前函数内部的全局变量。如代码段1:
上面的代码中,用 var 定义一个全局变量 value,在函数内部也同样用 var 声明一个函数内部的全局变量 value ,在 getValue() 的时候,返回函数内部的 value 变量值 backValue,直接访问 value 变量时访问的是最外层全局的 value 值为 value。在来看下面这段代码。如代码段2:
这段代码和上面代码段1项比较就是在getValue()方法内部没用用 var 声明 value ,此时函数内部访问的是最外层全局的value ,一次执行 getValue() 函数时候,将最外层全局的value值改变成了 backValue,后面正常输出。接下来再看下面的代码段3:
这段代码中,在getValue()方法中访问 value 变量,并返回其对应的值,但是函数内部作用域并没有声明该变量,一次js引擎回帮助我们项上层作用域中选择,即找到了最外层的全局作用域,之后正常返回。理解了上面的代码片段,我们在来看下下面的代码段4:
这段代码得出结果不难理解,但是你知道为什么会是这样的结果吗,你知道js引擎在帮我们做了哪些事情吗?针对上面的代码段4 ,js引擎会对其代码的位置进行调整,如下代码段5:
不难看出,var 声明的变量在函数内部也会提升,先声明,初始值为undefined,在else分支内部也可以拿到 value 这个变量。
使用 var 关键字声明的变量,无论其实际声明位置在何处,都会被视为声明于所在函数的顶部,如果不在任意函数内,则视为全局作用域的顶部,这就是所谓的变量提升。
使用var 声明的变量,不在任意函数内部的时候,相当于给全局window 对象添加一个属性,这导致使用 var 可能无意间覆盖 window对象的已有属性。如果你在全局作用域上使用let 或者const 创建新的变量,虽然 全局作用域上会创建新的绑定,但是不会有任何属性被添加到全局window对象上,这就以为着不能使用let 或者const 来覆盖一个全局变量,只能将其屏蔽。如下面代码段6:
es6 引入了let 声明的块级作用域,用let 所声明的变量在指定的作用域外无法访问,块级作用域的创建方式如下:
a、在函数内部创建
b、在一个代码块中创建,比如 if...else...中的花括号内。
仔细阅读如下代码段7:
上面的代码,在if 分支中用let 声明一个value变量,属于块级变量,因此在else 分支中是无法访问的,因此执行else 分支的代码时,访问value 变量报错,value is not defined 。同样在 if...else...结束了判断后也是无法访问这个变量的。
let 不像 var 一样,let 不能在同意作用域中重复声明一个已有标识。如下代码段8:
只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量,暂时性死区知识块级作用域中的一个特殊性质。如下代码段9:
先来看下下面代码段10:
for循环中使用 var 声明一个a 变量,相当于在外层作用域中先声明一个a变量,值为undefined,然后在每次循环中给 a 赋值,并访问 a 的值,但是如果使用 let 声明一个变量 i ,i只属于for 循环的块级作用域,循环结束后任意位置都不能访问该变量。再次访问就会报错 。
代码段11如下:
这段代码输出十次10,是因为循环内创建的函数都拥有对同一个变量的引用,循环结束的时候,全局的i变量值为10,因此每次访问 func()这个方法每次都输出10。在来看下用 let 的时候,代码段12如下:
使用了let之后,在循环中let声明的变量,每次都会创建一个新的i变量,因此在循环中创建的函数获得了各自的 i 的副本,而每个 i 副本都在每次循环迭代声明变量的时候被确定了。同样这种方式适用于 for -of ,for -in中,如下代码段13:
const 和let 一样,声明的变量都属于块级作用域,因此const具有 let 相同的特性,除此之外,还有一些特性如下:
const在声明变量初始化的时候必须赋值,例如:
如上面的代码,const声明对象的时候,对象的值是可以被修改的,const会阻止变量绑定和变量自身值的修改,但是无法阻止变量成员的修改,在 js 中,变量名存在在栈内存中,而变量对应的值存储在堆内存中,const只能阻止由栈内存指向堆内存的地址不发生改变,但是无法阻止堆中具体的数据发生改变。
上面的代码中 i 被定义为一个常量,第一次初始化时候赋值为 0 ,然后打印出0,之后执行 i++ ,改变 i 的值,语法报错,这个错误容易理解。
但是,const 在 for-of 和 for-in 中使用和 let 效果一样
(1)块级作用域
(2)变量提升
(3)暂时性死区
(4)重复声明
(5)是否属于window属性,覆盖问题
以上就是本文的全部内容,希望给读者带来些许的帮助和进步,方便的话点个关注,小白的成长之路会持续更新一些工作中常见的问题和技术点。