开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第一天,点击查看活动详情
原来JavaScript的let和var不能乱用
可能很多小白在刚学习JavaScript的时候在定义变量的时候会使用var或者let,也有一些开发者直接就只用var或者let,但是贸然使用var或者let其实在隐性的给你自己留bug,到时可能你找bug都可能找不到!!!
什么是var关键字
常常我们要定义一个变量会使用var关键字,后跟变量名:
//普通的定义一个变量
var test;
//初始化一个变量
var test2="我是用来测试的";
有时有些开发者会进行如下操作
var t="hi";
t=100
在这个例子中,变量t首先被定义为一个保存字符串值hi的变量,然后又被重写为保存了数值100。虽然不推荐改变变量保存值的类型,但这在ECMAScript中是完全有效的。
var 声明作用域
无疑var容易出错的地方也就是它的作用域,不出意外很多看前端面试题的开发者都可能会看到一些var变量相关的面试,而且很多时候他们的答案总是让人出乎意料,例如下面这道题
function test()
{
var message = "hi";
}
test();
console.log(message);
function test2()
{
message2 = "hi";
}
test2();
console.log(message2);
上面的打印结果会是什么?
首先我们要先明确一点,var的作用域是怎么定义的?
使用var在一个函数内部定义一个变量,就意味着该变量将在函数退出时被销毁:
接下来我们在回顾到上面那道简单的面试题
这里,message变量是在函数内部使用var定义的。函数叫test(),调用它会创建这个变量并给它赋值。调用之后变量随即被销毁,因此示例中的最后一行会导致错误。去掉之前的var操作符之后,message就变成了全局变量。只要调用一次函数test(),就会定义这个变量,并且可以在函数外部访问到。
注意:虽然可以通过省略var操作符定义全局变量,但不推荐这么做。在局部作用域中定义的全局变量很难维护,也会造成困惑。这是因为不能一下子断定省略var是不是有意而为之。在严格模式下,如果像这样给未声明的变量赋值,则会导致抛出ReferenceError。
要不再来一道简单的面试题
function foo()
{
console.log(age);
var age = 26;
}
foo();
这段代码会报错?
之所以不会报错,是因为ECMAScript运行时把它看成等价于如下代码:
function foo()
{
var age;
console.log(age);
age = 26;
}
foo();
这就是所谓的“提升”(hoist),也就是把所有变量声明都拉到函数作用域的顶部。此外,反复多次使用var声明同一个变量也没有问题:
什么是let
let跟var的作用差不多,但有着非常重要的区别。最明显的区别是,let声明的范围是块作用域,而var声明的范围是函数作用域。
if (true) {
var name = 'Matt';
console.log(name); // Matt
}
console.log(name); // Matt
if (true) {
let age = 26;
console.log(age); // 26
}
console.log(age); // ReferenceError: age没有定义
看到这段代码就很快会有人联系到一个东西,也就是for循环,因为很多时候我们会定义一个局部变量i作为索引,但是var和let定义这个变量i确实是有很大区别的,很多时候我们都是想着变量i循环完后就不在使用它也就是for结束变量i就应该被释放掉,但是如果我们用var去定义变量i的话则会出现以下问题
也就是变量i并没有被释放掉,这也可能会给后续带来一些隐藏的bug,解决这个问题我们就可以使用let来定义变量i
这样变量i就会在for循环结束后被释放掉
let没有提升
let与var的另一个重要的区别,就是let声明的变量不会在作用域中被提升。
在解析代码时,JavaScript引擎也会注意出现在块后面的let声明,只不过在此之前不能以任何方式来引用未声明的变量。在let声明之前的执行瞬间被称为“暂时性死区”(temporal dead zone),在此阶段引用任何后面才声明的变量都会抛出ReferenceError。
let和var,const最佳实践
不使用var
有了let和const,大多数开发者会发现自己不再需要var了。限制自己只使用let和const有助于提升代码质量,因为变量有了明确的作用域、声明位置,以及不变的值。
const优先,let次之
使用const声明可以让浏览器运行时强制保持变量不变,也可以让静态代码分析工具提前发现不合法的赋值操作。因此,很多开发者认为应该优先使用const来声明变量,只在提前知道未来会有修改时,再使用let。这样可以让开发者更有信心地推断某些变量的值永远不会变,同时也能迅速发现因意外赋值导致的非预期行为。