开辟一个内存空间,给这个内存空间贴一个标签,标签即变量名,通过变量名,可以访问到该变量对应开辟的内存空间。
let
const
var
类似 let ,老的JS中声明变量的唯一方式。
let,const 和 var的差异(重点)
let const有块级作用域,而var没有。什么是块? 简单理解即 >
{}包裹的代码块。 ```javascript if(true){ var test = true } console.log(test) // 正常打印true
if(true) { let test = true } console.log(test) // 报错:test is not defined
- **var**相比let和const,允许**重复声明**。- **var**声明的变量,可以再其声明语句之前被使用(**变量提升,hosting,但赋值停留原地**)。而**let, const具有暂时性死区**,会报错。> MDN的定义,在变量初始化前访问该变量会导致引用错误,该变量处在一个自块顶部到初始化处理的暂存死区中。```javascriptconsole.log(a)var a = 1;// 经过hosting相当于var a;console.log(a) // 打印undefineda = 1 // 赋值停留原地// 甚至这样也会提升,因为var无视代码块,会提升到当前函数作用域顶部function sayHi() {a = 'hello'if(false) { // 这个if代码块无效,var直接提升到sayHi函数最顶部var a;}console.log(a) // 打印hello}
暂时性死区和此法作用域的例子
function foo() {var a = 1;if(a) {// 这里的let a劫持了if块,而a + 2中的a访问的是if块内的a,而不是外层的var a,// 在还未初始化时就访问,因此报错。let a = a + 2}}foo() // 报错,Cannot access 'a' before initialization。a初始化前不能访问
demo(经典)
经常会遇到的一道面试题,通常用于考察闭包的使用。如下展示借助块级作用域
for(var i = 0; i < 10; i++) {setTimeout(() => {console.log(i)}, 1000)}// 打印10个10。// 原因在于 var 的没有块级作用域,1秒后,i值变为了10,10个定时器此时访问的是同一个i,因此打印10。// 使用let,劫持块级作用域for(let i = 0; i < 10; i ++) {// 同上} // 顺序打印0 到 9// 注意如果你把let i = 0 提到外面,则不行,因为没有劫持到每个循环的块。let i = 0;for(; i < 10; i ++) {// 同上} // 打印10个10。
IIFE
ES6前的时代,只有函数作用域和全局作用域,因此借助函数作用域模仿块级作用域,这种方式叫立即调用函数。
function() {}() //报错,函数声明需要一个函数名function name() {}() // 报错,语法错误,JS不允许立即调用函数// 所以通过()将函数声明包裹,告诉JS,这是一个函数表达式可以直接调用。(function() {// 函数体})()// 所以上面的demo也可以借助IIFE来完成。for(var i = 0; i ++; i < 10) {setTimeout((function d(i) {console.log(i)})(i), // 每个IIFE会访问创建时传入给自己的变量i,而不会访问到最外层的i1000)}
变量命名
驼峰规则
let a = 'isMyBook' 除首字母外的单词的首字母大写。
常量命名
通常固定的,不会变值称为常量。通常是大写命名。 const PI = '3.14' 。当然通过计算出来的值,我们还是采用驼峰命名即可 const a = 1 + 2 ,因为并不是在计算机运算前就知道的,而 π 这个是世界公认的无需计算的值。
