在ES6标准中JS对变量声明语法增加了let和const关键字,但是在工作中我还是常常发现很多朋友习惯于var,部分朋友可能会说“哦!我知道let,它用来声明局部变量。const的话,当然就是常量咯。”可是真的就是这样简单吗?那为什么很多人还是喜欢用var,或者仅仅只是在花括号中使用let?本片文章我们就这三个变量声明Key进行说明。
什么是变量声明和变量初始化?
我们先从变量初始化说起,相信这个问题很多朋友都可以正确回答出来,但我觉得还是需要在这里说明一下。先看看下面的代码:
<script>
// 变量声明
var codertoy;
document.writeln(codertoy); // undefined
document.writeln('<br />');
// 变量初始化
codertoy = '码农玩具';
document.writeln(codertoy); // 码农玩具
</script>
我们可以看到我们在页面显示第一个codertoy变量时结果是“undefined”,在我们给codertoy赋值之后再次调用codertoy变量结果是“码农玩具”。所以变量初始化应该是给变量一个具体的值,如果我们只是写了上面例子中的第二行的代码,这只是说明我们声明了一个变量,这里我们稍作扩展,再来看看下面的例子:
<script>
document.writeln(codertoy); // ReferenceError
</script>
这个例子中我们直接调用codertoy,我们发现提示“ReferenceError”引用错误,这是因为我们并没有声明过codertoy这个变量。我们继续看下面一个例子:
<script>
document.writeln(codertoy); // undefined
// 变量声明
var codertoy;
</script>
我们发现这次并没有引用错误提示,而是显示“undefined”,这说明在JavaScript中变量声明并不一定要在引用之前。下面继续看一个例子:
<script>
document.writeln(codertoy); // 码农玩具
// 变量初始化
codertoy = '码农玩具';
</script>
这个例子中我们并没有使用var关键字,而是直接给codertoy赋值,浏览器给我们正常显示出了“码农玩具”。那我们来总结一下:
-
变量声明后默认值是undefined;
-
变量初始化会先进行隐式变量声明,然后给变量赋值;
-
变量声明和变量初始化可以在JavaScript中的任意位置,与顺序无关;
什么是Scope(作用域)?
Scope这个词我们可以在各类语言中都可以见到,这么多语言都在使用Scope这一概念,说明它真的相当重要,所以我们必须要搞清楚它。这篇博文我对这个概念仅作基本介绍,稍后的其他博文再对Scope做进一步说明。
我们用一个例子来说明Scope。一栋大楼,大楼本身、大楼的每一层、每一个房间都可以看作一个Scope,我们叫大楼本身的Scope为全局Scope,每一层为层级Scope,每一个房间为块级Scope。在这个例子中,全局Scope包含了层级Scope,层级Scope包含了块级Scope。所以Scope其实就是一个域,每个域中可以有自己的变量和函数,域对同级和父级的域,它的变量和函数是不可见的,但子集的域可以访问父级域的变量和函数。看下面的代码:
function getCodertoy () {
var codertoy = '码农玩具';
return codertoy;
}
getCodertoy();
console.log(codertoy); // Reference Error
在上面的例子中codertoy变量声明在getCodertoy这个函数域中,所以在函数作用域的外面直接调用codertoy会报引用错误。
如何使用var、let和const?
有了上面的内容做铺垫,我们现在开始聊聊var、let和const这三者的区别。先看下面一个例子:
<script>
function getCodertoy() {
var codertoy = '码农玩具';
return codertoy;
}
getCodertoy();
document.write(codertoy); // Reference Error
</script>
这个例子中我们在getCodertoy函数作用域中声明了一个codertoy变量,在函数作用域外面调用这个变量,得到引用错误提示。说明var并非是直接声明一个全局变量。我们再看看下面一个例子:
<script>
for(var i=0; i< 5; i++) {
document.write(i);
}
document.write(i);
// 012345
</script>
这个例子中我们最终获得了“012345”的结果,哇哦!这是怎么回事?是不是与你期望的“01234”以及一个“Reference Error”提示不一样呢?那么这就是了,这里会涉及JavaScript参数引用声明这一知识点,这里我先不讲了。我们会发现如果我把var换成let,那么得到的结果就和我们预期的一样啦。我们总结一下:
-
在函数作用域中使用var和let在函数作用域外调用都是不可以的;
-
在for循环这种作用域中使用var声明的变量可以在for循环之外继续使用,但let声明的变量则不能在for循环之外继续使用。
var和let的另外一个区别是let声明的函数并不会执行声明提升,声明提升(Hoisting)。我们看下面一个例子:
<script>
document.write(codertoy); // Reference Error
let codertoy = '码农玩具';
</script>
还记得我们在上面讲变量声明和变量初始化的时候举的一个关于var的类似例子吗?如果我们使用var进行变量声明,则不会报引用错误,而我们使用let就会得到这个提示。
建议:优先于var使用let作为变量声明的原则,这样可以防止全局作用域变量污染。
什么时候用const呢?const的作用与let基本相同,不过很多朋友会将const理解为常量,其实这个关键字声明的变量准确的应该说是不可重复赋值的变量。常量是赋值之后不可以修改,而不可重复赋值的意思就是只可以赋值一次,但可以修改值。是不是优点困惑这样的解释?我们先来看看例子:
<script>
const codertoy = {
name:'码农玩具'
};
codertoy.name = 'Coder Toy';
document.write(codertoy.name); // Coder Toy
codertoy = 'Coder Toy'; // Type Error
</script>
我们可以看到我们成功的修改了codertoy.name的值,但当我们想给codertoy变量重新赋值时却提示我们“Type Error”类型错误。所以大家应该明白常量和不能重新赋值的区别了吧。
建议:优先使用const,根据需要使用let,最后使用var。