作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突。
一、 var变量及其作用域
作用范围:
函数作用域,存在变量提升,即实际解析顺序与编码位置无关(赋值前预解析)
var 变量的作用域无非就是两种:全局变量和局部变量。
全局作用域
定义: 在所有函数之外的变量,函数内部可以直接读取全局变量。
var name = "outer";
function fn(){
console.log(name);
}
fn(); //outer
局部作用域
定义: 一般只在固定的代码片段内可访问到,而对于函数外部是无法访问的,最常见的例如函数内部
function fn(){
var name = "inner";
}
fn();
console.log(name); // ReferenceError: name is not defined
注意
1. 不用var定义出来的是全局变量
函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!
function fn(){
name = "inner";
}
fn();
console.log(name); // inner
2. 变量提升
只要函数内定义了一个局部变量,函数在解析的时候都会将这个变量“提前声明”
var name = "global";
function fn(){
console.log(name); // undefined
var name = "local";
console.log(name);// local
}
fn();
相当于:
var name = "global";
function fn(){
var name; // 提前声明了局部变量
console.log(name);// undefined
name = "local";
console.log(name);// local
}
fn();
3. 块语句不会创建一个新的作用域
块语句(大括号“{}”中间的语句),如 if 和 switch 条件语句或 for 和 while 循环语句,不像函数,它们不会创建一个新的作用域
if (true) {
// 'if' 条件语句块不会创建一个新的作用域
var name = 'Hammad'; // name 依然在全局作用域中
}
console.log(name); // logs 'Hammad'
整体再来测试下,你会了?
var outVariable = "我是最外层变量";
function outFun() {
var inVariable = "内层变量";
variable = "未定义直接赋值的变量";
function innerFun() { //内层函数
console.log(inVariable);
}
innerFun();
}
console.log(outVariable); //我是最外层变量
console.log(variable);//variable is not defined
outFun(); //内层变量,要先执行这个函数,否则根本不知道里面是啥
console.log(variable);//未定义直接赋值的变量
console.log(inVariable); //inVariable is not defined
innerFun(); //innerFun is not defined
小结
- 不用var定义出来的是全局变量;
- 用var在全局范围内定义出来的也是全局变量;
- 用var在局部范围内定义出来的是局部变量。
二、 let变量及其作用域
作用范围:
块级作用域,不存在变量提升,不允许重复声明。
块级作用域
定义: 块语句(大括号“{}”中间的语句),如 if 和 switch 条件语句或 for 和 while 循环语句都会创建一个新的作用域
function func(arr) {
for (let i = 0; i < arr.length; i++) {
// 这里可以访问i ...
}
// 这里访问不到i
}
注意
1.不存在变量提升
var
命令会发生“变量提升”的现象,即变量可以在声明之前使用,值为undefined。这
种现象多少有些奇怪,按照一般的逻辑,变量应该在声明语句之后才可以使用,为了纠正这种现象,let命令
改变了语法行为,他所声明的变量一定要在声明后使用,否则报错。
function func() {
// name先使用后声明,报语法错
alert(name)
let name;
}
2.不允许重复声明
let不允许在相同的作用域内重复声明同一个变量
// 两个let重复声明
let age = 24;
let age = 30;
console.log(age) // 执行时报语法错
3.暂时性死区
暂时性死区本质就是:只要进入作用域,所要使用的变量就已经存在了,但是不可以获取,必须等到声明变量的哪一行代码出现。
var tmp = 123;
if (true) {
tmp = "abc"; //报错ReferenceError
let tmp;
}
三、 var和let的区别
示例
下面的代码中,变量i是var声明的,所以i是一个全局变量在全局范围内都有效,所以全局只有一个变量i,每一次循环i的值都会发生改变,被赋给数组a的函数内部的console.log(i)中的i指向全局的i,因此所有数组a的成员中的i指向的都是同一个i,导致运行时输出的是最后一轮的i值10
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
}
}
a[6]();//10
当我们使用let再来观察一下,变量i是let声明,当前的i只在本轮循环中有效,所以每一次循环都是一个新的变量,于是最后输出6。
var a = [];
for (let i = 0; i < 10; i++) {
console.log(i);
a[i] = function () {
console.log(i);
}
}
a[6]();//6
四、总结
var声明变量:
-
有变量提升
-
没有块级作用域,是函数作用域
-
能重复声明
-
可以重新赋值
let声明变量:
-
没有变量提升
-
有块级作用域
-
不能重复声明
-
可以重新赋值