【JS学习笔记】三种变量声明

这是我参与更文挑战的第1天,活动详情查看: 更文挑战

变量声明

有var、let、const三个关键字,可以声明变量。

var

变量的声明会自动提升到作用域顶部,也就说预编译时(通篇扫描一遍),把变量声明的语句提到最前面执行。

有函数作用域(局部作用域)和全局作用域。在同一个作用域中var多次声明同一个变量,词法分析中与声明一次没有区别。

globalVar = "隐式声明一个变量";
复制代码

如果globalVar从未出现过,那这里就是隐式声明变量后立即赋值。

隐式声明的变量会自动变成全局变量,是window的属性。

通过var定义的全局变量和函数都会成为window对象的属性和方法。

let

声明

不能重复声明同一个变量,也不能在声明之前调用它。js引擎会记录用于变量声明的标识符及其所在的块级作用域。

混用var、let声明同一个标识符也会报错,var、let这两个关键字并不是不同类型的变量,只是指出变量所在的作用域。

不会挂载到全局对象(window)上。

声明范围是块级作用域,被{ }所包含的范围。

块级作用域是函数作用域的子集。

if(true) {
    let age = 18;
}
复制代码

let声明的变量只存活在{}中。

外部无法访问age变量。

所以在类似try/catch、if/else语句中,let只存在块级作用域中。

for循环特殊处理

let声明的变量在for循环中进行特殊处理。

for(let i = 0; i < 10; i++) {
    setTimeOut(() => console.log(i), 0);
}
// 0 1 2 3 4 5 6 7 8 9
复制代码

因为let变量和块级作用域是绑定在一起的,块级作用域结束,let变量也就消失了。

每次进入循环体,都会开启一个新的作用域,并且将迭代变量绑定到该作用域(每次循环,使用的是一个全新的迭代变量)。

所以每一次箭头函数被setTimeout保存到外部时,连接的块级作用域都是不一样的,里面都有一个全新的i

迭代变量的作用域仅限于for循环块内部,在循环结束后会销毁。


for(var i = 0; i < 10; i++) {
    setTimeout(() => console.log(i), 0);
}
// 10 10 10 10 10 10 10 10 10 10
复制代码

var声明的变量,会渗透到循环体外部。不会创建一个全新的i

所以函数每次保存的都是同一个作用域,调用的都是同一个i

暂时性死区

name = "111";  // 会报错
let name = "Herb";
复制代码

个人理解:预编译时,let声明语句let name也会提升,只是把这个变量name存放在暂时性死区中,不会被赋予初始值。等到执行时,如果在声明之前调用name就会报错,直到执行到let name时,把name从暂时性死区中释放出来。

typeof name;
let name = "Herb";
// Cannot access 'name' before initialization
复制代码

所以也无法在name定义之前调用typeof。

const

const声明变量时,必须初始化。

尝试修改,会导致运行时错误。

块级作用域中。

const变量是引用数据类型(object、array、function等)时,不能再赋值为其他引用值,但可以对里面的内容进行更改。

如果希望对象里面的内容也不能更改的话,可以使用Object.freeze(),虽然赋值时不会报错,但会静默失败。

const obj = { name: "Herb" };
obj = Object.freeze(obj);
obj.name = "hhhhh";
console.log(obj);
// { name: "Herb" }
复制代码

由于const声明暗示变量的值是单一类型且不可修改的,JavaScript运行时编译器可以将其所有实例都替换成实际的值,而不会通过查询表进行变量的查找。谷歌的V8引擎就执行这种优化。

在开发中应该尽可能地多使用const声明。

发现需要更改时再修改声明。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享