var关键字
作用:后接变量名,用来定义变量
var num
这里通过var关键字定义了一个num变量,我们没有给num初始化,此时num的值为undifined
var str ="kobe"
str=123
这里通过var关键字定义了一个保存“kobe”字符串值的 变量str,str的数据类型不是被规定死的,第二行的代码改变了存储值得同时改变了数据类型(改变类型是不推荐的)
声明范围(函数作用域)
- 全局作用域下
var str = "hello,word"
function test() {
console.log(str)
}
test() //hello ,world
在全局作用域下通过var关键字定义的str变量为全局变量
var num1 = 1
console.log(window.num1, window.num2) //1,undefined
var num2 = 2
console.log(window.num1, window.num2) //1,2
这里要注意使用var全局声明的变量会成为window对象的属性
- 函数体内:
function test(){
var str="hello, world"
}
console.log(str) //报错!
在函数体内通过var关键字定义str变量,函数被调用时会创建这个变量并给其赋值,同样在函数被执行完毕后变量被销毁。也就是说这个变量只在函数作用域内有效,在函数体外部我们是访问不到的。
- 函数体内省略var关键字
test()
console.log(str) //hello, world
function test() {
str = "hello,word"
}
同样是在函数体内定义,不同的是省略了var关键字,test函数被调用时(只要被调用一次)就会创建全局变量str,该变量和在直接全局作用域下定义的一样都会成为window的属性
变量提升
- 例1
console.log(str) //undefined
var str = "hello,word"
console.log(str) //hello, world
这里不会报错是相当于执行了如下代码
var str
console.log(str) //undefined
str="hello, world"
console.log(str) //hello, world
- 例2
var num = 1
var num = 2
console.log(num) //2
var num = 3
var num = 4
console.log(num) //4
相当于执行了如下代码
var num
num=1
num=2
console.log(num) //2
num=3
num=4
console.log(num) //4
这就是var关键字存在的变量“提升”,也就是把所有变量声明拉倒作用域的顶部。
let关键字
let关键字和var关键字的作用类似,不过相比于var,let显得更加的严谨,let关键字不存在“变量提升”,let关键声明的作用域范围是块作用域而var关键字是函数作用域
作用:后接变量名,定义变量
let str
str = 123
console.log(str) //123
str = "kobe"
console.log(str) //kobe
声明范围(块级作用域)
{
var num = 1
}
console.log(num) //1
{
let num = 1
console.log(num) //1
}
console.log(num) //报错!
正如开头所说的let关键字不同于var关键字 他的声明范围为块级作用域,在作用域外是访问不到该变量的
var num=1
{
let num=2
console.lgo(num) //2
}
{
var num = 1
let num = 2
console.log(num) //报错
}
{
let num=1
let num=2
console.log(num) //报错
}
对于let关键字在同一块级作用域中,相同标识符会报错
与var关键字不同 ,let关键字在全局作用域声明的变量不会成为window对象的属性
暂时性死区
相比于var关键字,let声明的变量不会在作用域中被提升。
console.log(num) //报错! 这里不会和var一样打印undefined
let num=1
在num被声明前执行的一瞬间成为“暂时性死区”
经典例题
for(var i=0;i<5;i++){
}
console.log(i) //5
通过var关键字定义的迭代变量会渗透到循环体外部
for(let i=0;i<5;i++){
}
console.log(i) //报错!
迭代变量的作用域是循环体这个块级作用域,let关键字定义的变量范围正是块级作用域,所以外部访问会报错
for (var i = 0; i < 5; i++) {
console.log(i)
}
//0 1 2 3 4
for(var i=0;i<5;i++){
setTimeout(()=>{console.log(i)},0)
}
// 5
加了定时器函数后打印结果变了,这是因为在退出循环时迭代变量保存的是导致循环退出的值:5,在之后的执行超时函数时所有的i都是同一个值:5
for(let i=0;i<5;i++){
setTimeout(()=>{console.log(i)},0)
}
//打印结果0 1 2 3 4
这是因为后台会为个迭代循环声明一个新的迭代变量,他们都是互相独立的,每一个超时函数时打印的都是不同的变量实例
const关键字
const关键字和let是类似的,唯一 一个重要区别是const关键字声明变量时必须初始化,并且在修改const定义的变量时会报错(这里指的是修改内存地址),在修改一个对象内部的一个属性时是不会报错的