2_2:语言基础:变量
重点理论
JS 执行代码分为两个阶段
1. 词法分析:包括 分析变量声明 分析函数声明 分析形参 三个部分
2. 执行阶段
JS解析引擎通过语法分析将我们写的JS代码转成可执行的代码,然后再执行
一 基本概念
1)js中的变量
1. js的变量是松散类型的 变量可以保存任何类型的数据.
2. 每个变量只不过是一个用于保存任意值的命名占位符
3. var 在ECMAScript所有版本中都可以使用 而 [const 和 let] 只能在Es6及更晚的版本中使用
4. 变量不初始化的情况下 会保存一个特殊值 undefined
5. 如果定义多个变量 可以在一条语句中用逗号分隔每个变量
var age = 100, sex = 'man', height = '180';
2)变量提升
1. 在代码执行前 解释引擎会 把所有的 变量声明语句 提升到作用域的最顶部 (赋值语句不会提升)
也就是说 我们可以先使用 再声明变量
2. 变量声明如果不使用var关键字 那么这个变量在执行过程中不会被提升
console.log(age);
age = 100;
//ReferenceError: age is not defined
3)全局作用域
. 直接写在 script 标签中的JS代码 都是全局作用域
. 全局作用域在页面打开时创建 在页面关闭时销毁
. 在全局作用域中有一个 全局对象 window 它代表一个浏览器的窗口 由浏览器创建 我们可以直接使用
. 在全局作用域中 所有的变量都会成为 window对象的属性 [let 和 const 定义的除外 ]
所有的函数都会成为 window对象的方法
. 在全局作用域下创建的变量是全局变量 整个页面都可以使用
4)函数作用域
. 调用函数时创建函数作用域 函数执行完毕后 函数作用域销毁
. 函数每调用一次 就创建一个函数作用域 它们之间是相互独立的
. 在函数作用域中可以访问到全局作用域的变量 但全局作用域访问不到函数作用域的变量
. 但函数在操作一个变量的时候 会先在自身作用域寻找 如果找到直接使用
如果找不到 则向上一级作用域查找 直到全局作用域
如果全局也找不到 则会报错
. 在函数中 省略var关键字 声明的变量是全局变量 [在严格模式下 给未声明的变量赋值 会报错]
. 定义形参 就相当于在函数中声明了变量
5)块级作用域
. 块作用域由 { } 包括
. 块级作用域是函数作用域的子集
. if语句和for语句里面的{ }也属于块作用域。
. 用let命令新增了块级作用域 外层作用域无法获取到内层作用域 非常的安全明了.
. 即使外层和内存都使用相同变量名 也都互不干扰
1)var let const 三者的区别
1. 作用域的区别
var 声明的变量是 函数作用域
let const 声明的变量是 块级作用域
2. 变量提升的区别
var声明的变量 存在 变量提升
let const 声明的变量 不存在 变量提升
3. 冗余声明的区别
var 不存在冗余声明 后声明的变量会覆盖前面的变量
let const 不允许冗余声明
----------------------------------------
var 和 let 混用也是不可以的
代码如下:
var age;
let age;
在代码中 var 和 let 声明的 age 是同一个变量 而非不同变量
它们只是指出 变量在相关作用域如何存在
4. window对象的区别
var 声明的全局变量 会自动挂载到window对象上
let const 声明的变量则不会
2)let 独有的特性
2.1)暂时性死区
![](https://img2020.cnblogs.com/blog/1727725/202105/1727725-20210525074610394-2008915073.png)
在解析代码时,JavaScript引擎也会注意到后面的let声明,只不过在此之前以任何方式来引用未声明的变量.因为let声明的变量不存在 变量提升.
在let声明之前的执行瞬间被称为 暂时性死区,在此阶段引用任何后面才声明的变量都会报错
2.2)for循环中的 var 和 let
//. var 定义的迭代变量 会渗透到循环体外 for (var i=0; i<5;i++) {} console.log(i);//打印5 因为{}里用var定义的变量是全局变量 //. 使用let定义变量 就不存在这个问题了 因为let定义的是块作用域 for (let i=0; i<5;i++) {} console.log(i);//i is not defined
for 循环中执行异步
//使用var声明迭代变量
最常见的问题是 对迭代变量的奇特声明和修改
for (var i=0; i<5;i++) { setTimeout(function () { console.log(i) console.log('------') },0); } //输出 5 5 5 5 5 而非 0 1 2 3 4 // 在退出循环时 迭代变量保存的是导致循环退出的值5 在之后执行超时逻辑时 所有的i都是同一个变量 因而输出的都是最终值
-----------------------------------------------------------------------------------------
//使用let声明迭代变量
for (let i=0; i<5;i++) { setTimeout(function () { console.log(i) console.log('------') },0); }//输出 0 1 2 3 4 而非 5 5 5 5 5 // 使用let声明迭代变量时 JavaScript后台会为每一个迭代循环声明一个新的迭代变量 // 每个setTimeout 引用的都是不同的变量实例 所以console.log输出了我们期望的值 // 也就是循环过程中每个迭代变量的值
3)const 独有的特性
1. const 声明变量的同时必须同时初始化 不能通过null占位
2. 声明后不能再修改 [ 不能修改变量的引用 ]
3. 如果const 变量引用的是一个对象 则可以修改对象内部的属性
4. 不能用const来声明 会自增的迭代变量 比如 for循环中的变量 i
5. 但是 可以使用在 for of 和 for in 的遍历上 [ 下面代码会详细说明 ]
3.1)代码范例:不能用在 for循环中
for (const i=0; i<10;i++) { console.log(i) }// 报错 Assignment to constant variable. 常量变量赋值。 // for循环中的迭代变量可以使用let声明 但是不能用const // 这也非常好理解 const声明的常量 而迭代变量会自增或自减
3.2)const 可以使用在for in 和 for of 迭代中
. for in 循环 迭代对象或数组的 属性名
. for of 循环 迭代对象或数组的 属性值
比较有意思的点:
我们把迭代变量声明在for循环的小括号内 但实际上代码在执行的时候 它是在循环体花括号内部声明变量 每次迭代的时候都会创建一个新的作用域 在新作用域下声明一个新的常量 所以迭代变量的值并没有被修改 这是在for in 和 for of 中可以使用const的原因
for in 代码范例
//for in 遍历数组 var arr = ['张三','李四','王五','赵六']; for (const index in arr ) { console.log(index);//打印 0 1 2 3 } //for in 遍历对象 var obj = {user:'张三',age:100,sex:'man'}; for (const index in obj) { console.log(index);//打印 user age sex } //for in 变量字符串 for (const index in '北京欢迎您') { console.log(index);//打印 0 1 2 3 4 }
for of 代码范例
//for of 遍历数组 var arr = ['张三','李四','王五','赵六']; for (const index of arr ) { console.log(index);//打印 张三 李四 王五 赵六 } //for of 变量字符串 for (const index of '北京欢迎您') { console.log(index);//打印 北 京 欢 迎 您 } //for of 不能遍历普通对象遍历对象 var obj = {user:'张三',age:100,sex:'man'}; for (const index of obj) { console.log(index);//打印 user age sex }//会报错
4)最佳实践
. ES6 增加let和const 从客观上为这门语言更精准地 声明作用域 和 语义 提供了更好的支持.
1. 不使用var 限制自己只使用 let 和 const 有助于提升代码质量. 因为变量有了明确的作用域 声明位置 以及不变的值
2. const 优先 let 次之
使用const声明可以让浏览器运行时 强制保持变量不变 也可以让静态代码分析工具 提前发现不合法的赋值操作。
因此我们应该优先使用const来声明变量 只在提前知道未来会有修改时 再使用let关键字.