须知:
1.什么是变量提升?
Java****Script 中,函数及变量的声明都将被提升到函数的最顶部。
JavaScript 中,变量可以在使用后声明,也就是变量可以先使用再声明。
2**.全局作用域 :变量在函数外定义,即为****全局变量**。全局变量有 全局作用域: 网页中所有脚本和函数均可使用。
局部作用域:变量在函数内声明,变量为****局部作用域。局部变量:只能在函数内部访问(在{}内有效,{}外是无效的)。
面向对象原则:高内聚、低耦合。多聚合、少继承
暂时性死区:只要一进入当前作用域,所要使用的变量就已经存在,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
var命令
1.声明一个函数作用域或全局作用域变量,可以选择将其初始化为一个值。
2.它可以是嵌套的函数,或者对于声明在任何函数外的变量来说是全局。如果你重新声明一个 JavaScript 变量,它将不会丢失其值。var定义的变量会预解析,简单的说就是如果变量没有定义就直接使用
3.只声明,不赋值,结果undefined
var age;console.log(age);//undefined
4.不声明不赋值,直接使用,报错
console.log(age);//报错
5.不声明,只赋值,返回结果
var age=10;console.log(age);//10
let命令
- 循环体的let变量只对花括号作用域可见,花括号外不可见
- 循环体的语句部分是一个父作用域,而循环体内部是一个单独的子作用域
- let声明的变量不存在变量提升,未声明的使用会报错,暂时性死区
- 只要块级作用域内存在let声明,它所声明的变量就绑定了这个区域,不再受外部的影响
- let不允许在相同的作用域重复声明同一个变量,子父级作用域可以同名变量声明
const命令
- const常量的值一旦声明就不得改变
- const一旦声明变量,就必须立即初始化,不能留到以后赋值
- const的作用域与let命令相同,只在声明所在的块级作用域内有效
- const命令同样存在暂时性死区,只能在声明的位置后面使用
- const声明的常量,也与let一样不可重复声明
- 对于复合类型的变量,变量名不指向数据,而是指向数据所在的地址,所以const命令只是保证变量名指向的地址不变,并不保证该地址的数据不变
let和const命令的声明不再自动纳入global对象(window)
块级作用域与函数声明
- ES6的浏览器下,块级作用域内声明的函数会被提升至全局作用域或函数作用域顶部,作为
var fn = undefined - 其他的游览器下,还是将块级作用域的函数声明当作let处理
- 应该避免在块级作用域内声明函数,如果确实需要也应该使用函数表达式而不是函数声明
- ES6的块级作用域允许声明函数,但只在使用大括号的情况下成立,如果没有使用大括号就会报错
区别:
1.var声明变量存在变量提升,let和const不存在变量提升
console.log(a); // undefined ===> a已声明还没赋值,默认得到undefined值
var a = 1;
console.log(b); // 报错:b is not defined ===> 找不到b这个变量
let b = 2;
console.log(c); // 报错:c is not defined ===> 找不到c这个变量
const c = 3;
变量提升,不执行的代码也影响执行的代码
function fn() {
//var a
if (true) {
console.log(a+'b')
}
else {
var a = 1
console.log(2)
}
}
fn() // a -> undefined
2.全局变量var,块级局部变量let和const
var定义的变量在全局作用域上,全局共享,在方法中用定义的var只有该方法内生效。
块级局部变量,就是在当前代码块内有效。
{
let a=10;
}
console.log(a)//undefined
const声明时必须赋值
const a//报错
const只能赋值一次,声明后不能修改,常用来定义常量
**优先级var<let<const,**但是实际中最常用的是let
var定义变量容易造成全局污染,一旦被修改全局都要修改,造成低内聚,高耦合
首先const声明常量的好处,一是阅读代码的人立刻会意识到不应该修改这个值,二是防止了无意间修改变量值所导致的错误,另外其实js编译器也对const进行了优化,可以提高代码的执行效率;
另外let声明的变量没有预编译和变量升级的问题,先声明再使用其实更为规范,而let本身是一个块级作用域,很多时候我们在写代码的时候都希望变量在某个代码块内生效,也更为方便。
最后说一点就是使用的场景说明:let一般应用于基本数据类型;const 一般应用于引用数据类型,也就是函数对象等。
const a=10;
a=20;//报错(控制台,编译器)
3.同一个作用域下,let 可以更新,但不能重新声明,var 变量可以重新声明和更新(覆盖栈中的值),const 无法更新或重新声明
let a=1;
let a=3;
//报错(控制台,编译器)
面试题
for(let i=0;i<3;i++){
console.log(i)
}
//打印:0,1,2
//我再来问大家,此时i等于几?题解最后看答案
//回归正题
//正常理解是i在()作用域内,打印不出来,但是实现机制导致let的作用域不是我们理解的那样
//可以这样理解
for (let _i = 0; i < 5; i++) {
// 每次循环创建一个i
let i = _i
console.log(i)
//最后i++后再将变量i的值赋值回_i上, i++ 先做
_i = i
}
//答案i=3,因为i=3到这里就不执行了
隐秘死区
function fn (x = y , y = 2) {
return [x,y]
}
fn() // Error:Cannot access 'y' before initialization
//y没有被定义却被x使用了
var a = a;
let b = b;
作用域问题
if(true) {
// 暂时性死区开始
name = "cht" ;// Error
console.log(name); //Error
let name; // TDZ结束
console.log(name); // undefined
name = "hw";
console.log(name); // hw
}
----------------------------------------------------------------------------------------
!function fn() {
var a = 5;
}()
console.log(a); // Error:a is not defined 外部访问不到
// 等同于
{
var a = 5
}
注意:
- 块级作用域允许相互嵌套
- 外层作用域不能访问内层变量
- 不同层级作用域可以定义同名变量
- es6允许在块级作用域下声明函数,在块级作用域外面不可引用
- 凡是有{}者都有块级作用域
- ES6的块级作用域必须有大括号 {} 如果没有{} js引擎认为不存在块级作用域