一、ES5 变量定义
1.在全局作用域中 this 其实就是window对象
<script>
console.log(window === this)
</script>
输出结果: true
2.在全局作用域中用var定义一个变量其实就相当于在window上定义了一个属性
例如: var name = "孙悟空" 其实就相当于执行了 window.name = "孙悟空" 或者this.name = "孙悟空"
// 用var 定义一个变量其实就是在window上添加一个属性
var name = "孙悟空"
console.log(name)
console.log(window.name)
console.log(this.name)
执行结果:
孙悟空
孙悟空
孙悟空
3.变量提升 使用var声明变量,会自动提升到函数作用域顶部
可以分为两种情况,在全局作用域和在代码块或者方法中
<script>
// name 变量是在下面定义的,但是可以在上面使用 name 这个变量
// 这是因为变量被提升到作用域的最上面
console.log(name)
console.log(window.name)
console.log(this.name)
var name = "孙悟空"
</script>
运行结果:
空白
空白
空白
可以看到这里直接相当于在最上面定义了 var name 但是没有初始化,我感觉因该是输出undefined
但是这里啥也没输出,但是没有报错
这里测试的时候千万不要刷新页面,刷新就相当于前一次把name放入window对象中
function printName(){
console.log(name)
}
printName()
var name = "孙悟空"
运行结果:
空白
同样是因为在全局作用域使用var定义变量,就相当于在最上面定义了一个变量,其实就是在编译的时候,直接把这个变量放在window身上了,但是没有初始化,调用printName()的时候,还未执行初始化语句,所以输出空白
<script>
// 在代码块中
{
console.log(address)
var address = "花果山"
console.log(address)
}
// 在方法中
function defineSex(){
console.log(sex)
var sex = "男"
console.log(sex)
}
defineSex()
</script>
运行结果:
undefined
花果山
=====================================
undefined
男
这里没有报错只是输出了undefined,说明在执行的时候是存在这个变量的,只是没有进行初始化
以上代码相当于下面的代码
<script>
// 在代码块中
{
var address;
console.log(address)
address = "花果山"
console.log(address)
}
// 在方法中
function defineAge(){
var sex;
console.log(sex)
sex = "男"
console.log(sex)
}
defineAge()
</script>
4.在方法中用 var 定义的变量只在方法中有效,在方法外面是访问不到的,因为当方法执行完的时候,在其中用var定义的变量就销毁了
<script>
function defineSex(){
var sex = "男"
console.log(sex)
}
defineSex()
console.log(sex)
</script>
运行结果:
男
Uncaught ReferenceError: sex is not defined
5.在代码块中用 var 定义的变量在代码快中有效,在代码块外面也能访问到
<script>
console.log(address)
if(true){
var address = "花果山"
console.log(address)
}
console.log(address)
</script>
运行结果:
undefined
花果山
花果山
<script>
console.log(address)
while(true){
var address = "花果山"
console.log(address)
break;
}
console.log(address)
</script>
运行结果:
undefined
花果山
花果山
6.不使用 var, 直接进行变量定义
<script>
// 虽然这里能够访问到 name 但是输出是空白,说明也是做了变量提升,将name放在最前面
// 但是由于没有初始化,所以不能输出孙悟空,这里有点好奇为啥不输出undefined
// 这种情况下大家测试的时候千万要重新开一个浏览器窗口,第二次刷新的时候就不准确了,
// 可以输出 孙悟空 ,因为第一次打开的时候,就将name放入 window 了,刷新就相当于
// window 中已经有 name 这个属性了
console.log(name)
// 在全局作用域不使用var直接定义的变量相当于全局变量,在上面也能访问到
name = "孙悟空"
function defineGlobal(){
// 在方法中不使用var直接定义的变量相当于全局变量,但是必须得是在这个方法被调用过之后
// 才可以在方法外面访问到这个全局变量
global = "我是全局变量"
}
defineGlobal()
console.log(global)
function defineGlobal1(){
global1 = "我是全局变量1"
}
// defineGlobal1 没有被调用所以在defineGlobal1 中定义的全局变量global1在方法
// 外面是访问不到的
console.log(global1)
</script>
运行结果:
空白没有报错
我是全局变量
Uncaught ReferenceError: global1 is not defined
7.在网上看到一个面试题挺有意思
<script>
// 变量提升将下面定义的 a 和 b 提到最上面,但是不初始化,所以这里a b 都是undefined
console.log("1", a, b); //1 undefined undefined
// 这里对 a b 进行初始化
var a = 12, b="34";
function foo(){
// 这里换了一个作用域,也就是在方法体内部
// 由于在这个作用域中又定义了一个 a 变量,所以 a 会被提到当前作用域(方法内)的最上面
// 但不会初始化,所以这里的 a 其实是方法中新定义的 a ,所以是undefined,
// 方法中没有重新定义 b,所以还用方法外面定义且初始化好的 b
console.log("2", a, b); //2 undefined 34
// 这里重新对方法中定义的 a 进行初始化
var a=b=12;
// 上面对方法中定义的 a 初始化,所以这里输出 方法中定义的 a 和 外面定义的 b 都是12
console.log("3", a, b); //3 12 12
}
foo()
//这里输出的是 外面定义的 a ,外面定义的 b ,由于b在方法中被修改为12
console.log("4", a, b); //4 12 12
</script>
运行结果:
1 undefined undefined
2 undefined 34
3 12 12
4 12 12
<script>
// 变量提升将下面定义的 a 和 b 提到最上面,但是不初始化,所以这里a b 都是undefined
console.log("1", a, b); //1 undefined undefined
// 这里对 a b 进行初始化
var a = 12, b="34";
function foo(){
// 这里换了一个作用域,也就是在方法体内部
// 由于在这个作用域中又定义了一个 a 变量,一个b变量,所以 重新定义的 a b 会被
// 提到当前作用域(方法内)的最上面
// 但不会初始化,所以这里的 a 和 b 其实是方法中新定义的 a b ,所以都是undefined,
console.log("2", a, b); //2 undefined undefined
// 这里重新对方法中定义的 a 进行初始化
var a=12 , b=12;
// 上面对方法中定义的 a 初始化,所以这里输出 方法中定义的 a 和 外面定义的 b 都是12
console.log("3", a, b); //3 12 12
}
foo()
// 这里输出的是外面定义的 a ,外面定义的 b ,这次方法中重新定义了一个b,所以外面的b没有被
// 修改
console.log("4", a, b); //4 12 34
</script>
运行结果:
1 undefined undefined
2 undefined undefined
3 12 12
4 12 34
8.可以在同一作用域中重复多次用 var 定义相同名称的变量
<script>
var name = "孙悟空"
var name = "猪八戒"
console.log(name)
</script>
运行结果:
猪八戒
没有报错
二、ES6 let 申明变量
1.块级作用域
<script>
console.log(address)
if(true){
let address = "花果山"
console.log(address)
}
console.log(address)
</script>
Uncaught ReferenceError: address is not defined
2.在同一作用域中不能重复定义
<script>
let name = "孙悟空"
let name = "猪八戒"
//Uncaught SyntaxError: Identifier 'name' has already been declared 了解此错误
</script>
<script>
function printName(){
let name = "孙悟空"
let name = "猪八戒"
}
//Uncaught SyntaxError: Identifier 'name' has already been declared了解此错误
</script>
<script>
let name = "孙悟空"
//
function printName(){
let name = "猪八戒"
console.log(name)
}
printName()
console.log(name)
</script>
没有报错,运行结果:
猪八戒
孙悟空
说明在不同的作用域可以定义相同名称的变量
<script>
let name = 1;
if(true){
let name = 2;
console.log(name)
}
console.log(name)
</script>
运行结果:
2
1
<script>
let address = "花果山"
var address = "猪八戒"
</script>
Uncaught SyntaxError: Identifier 'address' has already been declared
<script>
var address = "猪八戒"
let address = "花果山"
</script>
Uncaught SyntaxError: Identifier 'address' has already been declared
3.不存在变量提升
<script>
console.log(address)
let address = "猪八戒"
</script>
Uncaught ReferenceError: Cannot access 'address' before initialization
这里跟 var 有很大不同, var会进行变量提升,不会报错的
4.let 在全局作用域定义的变量是不会挂到window身上的
<script>
let address = "猪八戒"
console.log(window.address)
var name = "孙悟空"
console.log(window.name)
</script>
运行结果:
undefined
孙悟空
说明 var 在全局作用域定义的变量会挂到window身上, 但是let在全局作用域定义的变量不会挂到window身上
三、ES6 const 申明变量
1.声明变量时必须同时初始化
<script>
const name;
</script>
Uncaught SyntaxError: Missing initializer in const declaration
2.一旦申明完了不能修改
<script>
const name = "孙悟空";
name = "猪八戒";
</script>
Uncaught TypeError: Assignment to constant variable.
其实这里的不能修改指的是不能修改 const 变量指向的内存地址,但是修改内存地址里面的数据是可以的。
<script>
const TEAM = ["唐僧" ,"孙悟空" , "猪八戒"];
TEAM.push("沙僧")
TEAM.push("白龙马")
console.log(TEAM)
</script>
<script>
const TEAM = ["唐僧" ,"孙悟空" , "猪八戒"];
TEAM = ["唐僧" ,"孙悟空" , "猪八戒"];
</script>
Uncaught TypeError: Assignment to constant variable.
<script>
const person = {
name:"孙悟空",
address:"花果山"
}
person.weapon = "金箍棒"
console.log(person)
</script>
<script>
const person = {
name:"孙悟空",
address:"花果山"
}
person = {
name:"孙悟空",
address:"花果山"
}
</script>
Uncaught TypeError: Assignment to constant variable
var | let | const |
---|---|---|
函数级作用域 | 块级作用域 | 块级作用域 |
重复声明 | 不可重复声明 | 不可重复声明 |
变量提升 | 不存在变量提升 | 不存在变量提升 |
值可更改 | 值可更改 | 值不可更改 |
全局变量挂载到window | 全局变量不会挂载到window | 全局变量不会挂载到window |
for (var i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i);
}, 0);
}
console.log(window.i) // 6 var定义的变量会挂到 window 上
运行结果:
6
6
6
6
6
6
在每次循环迭代中,都会调用 setTimeout 函数,传入一个匿名函数(回调函数)和一个延迟时间
(这里是 0 毫秒)。尽管延迟时间设置为 0,这并不意味着回调函数会立即执行。相反,
它会被添加到 JavaScript 的事件队列中,等待当前执行栈清空后执行
// 如果要用var定义变量的话需要通过闭包的方式来解决
for (var i = 1; i <= 5; i++) {
(function(j) {
setTimeout(function () {
console.log(j);
}, 0);
})(i);
}
for (let i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i);
}, 0);
}
console.log(window.i) // undefined let定义的变量不会挂到window
运行结果:
undefined
1
2
3
4
5
下面这个例子要求当点击某个div的时候,将其背景颜色改成粉色
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
div{
border-style: inset;
border-color: black;
border-width: 1px;
height: 100px;
width: 100px;
float: left;
margin-left: 20px;
background-color: white;
}
</style>
</head>
<body>
<div></div><div></div><div></div>
</body>
<script>
var boxes = document.getElementsByTagName("div")
console.log(boxes)
console.log(boxes[0])
// 这里使用 let 定义 变量 i ,进行遍历没问题
for (let i = 0; i < boxes.length ; i++) {
boxes[i].onclick = function (){
boxes[i].style.background = 'pink'
}
}
</script>
</html>
<script>
var boxes = document.getElementsByTagName("div")
console.log(boxes)
console.log(boxes[0])
// 这里使用 var 定义 变量 i ,进行遍历就会报错
for (var i = 0; i < boxes.length ; i++) {
boxes[i].onclick = function (){
boxes[i].style.background = 'pink'
}
}
</script>
Uncaught TypeError: Cannot read properties of undefined (reading 'style')
这里主要原因是 var i 定义了一个全局变量,这个变量会挂到 window 上,当for循环执行完的时候,i === 3
,也就是我们点击的时候 i === 3,boxes只有3个元素,当我们点击的时候就会回调
boxes[i].style.background = 'pink' ,boxes[3]是undefined,所以会报错