一、全局作用域
用 var 在全局(函数外)声明的所有变量,都具有全局作用域,即: 网页中所有脚本和函数均可使用。
(1)最外层函数和在最外层函数外面定义的变量拥有全局作用域,例如:
var aName="哈哈";
function doSomething(){
var bName="嘿嘿";
function innerSay(){
alert(bName);
}
innerSay();
}
alert(aName); //哈哈
alert(bName); //脚本错误
doSomething(); //嘿嘿
innerSay() //脚本错误
(2)所有末定义直接赋值的变量自动声明为拥有全局作用域,例如:
function doSomething(){
var aName="哈哈";
bName="嘿嘿";
alert(aName);
}
doSomething(); //哈哈
alert(bName); //嘿嘿
alert(aName); //脚本错误
二、局部作用域
局部作用域一般只在固定的代码片段内可访问到,最常见的例如函数内部,例如下列代码中的bName和函数innerSay都只拥有局部作用域。在函数中用 var 声明的所有变量,都是函数的局部变量,具有局部作用域,即:变量只能在函数内部使用,函数外部是不行的
function doSomething(){
var bName="嘿嘿";
function innerSay(){
alert(bName);
}
innerSay();
}
alert(bName); //脚本错误
innerSay(); //脚本错误
三、存在的问题
在ES5中,只有全局作用域和函数作用域。这会导致函数作用域覆盖了全局作用域;亦或者循环中的变量泄露为全局变量。
(1)内层变量可能覆盖外层变量(变量提升)
var tmp = new Date();
function f(){
console.log(tmp);
var tmp = "hello";
}
f();//undefined
var tmp = new Date();
function f(){
var tmp;
console.log(tmp);
tmp = "hello";
}
f();//undefined
(2)用来计数的循环变量泄露为全局变量
for (var i = 0; i < 10; i++) {
console.log(i);
}
console.log(i); // 10
四、块级作用域
ES6中新增了块级作用域,块级作用域由 { } 包括,if语句和for语句里面的{ }也属于块级作用域。ES6可以使用let关键字或者const关键字来实现块级作用域。let或const声明的变量只在let或const命令所在的代码块{}内有效,在{}之外不能访问。
(1) 允许块级作用域任意嵌套,外层作用域无法读取内层作用域的变量
if(true){
let a = 10;
var b = 1;
if(true){
let c = 20;
console.log(a); //10
console.log(b); //1
}
console.log(c)// ReferenceError: c is not defined.
}
(2) 内层作用域可以定义外层作用域的同名变量
if(true){
let a = 10;
var b = 30;
if(true){
let a = 20;
var b = 40;
console.log(a);//20
console.log(b);//40
}
console.log(a);//10
console.log(b);//40
}
五、块级作用域之LET CONST
(1)ES6 新增了let和const命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let和const命令所在的代码块内有效。
if(1){
var a = 100;
let b = 10;
}
console.log(a); // 100
console.log(b) // 报错:b is not defined ===> 找不到b这个变量
for(let i =0; i<arr.length; i++){
}
console.log(i) //ReferenceError: i is not defined
var arr = [];
for (var i = 0; i < 10; i++) {
arr[i]=function(){
console.log(i);
}
}
arr[2](); //输出的是10
var arr = [];
for (let i = 0; i < 10; i++) {
arr[i]=function(){
console.log(i);
}
}
arr[2](); //输出的是2
(2)不存在变量提升,let和const命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。
console.log(a); // undefined ===> a已声明还没赋值,默认得到undefined值
var a = 100;
console.log(b); // 报错:b is not defined ===> 找不到b这个变量
let b = 10;
console.log(c); // 报错:c is not defined ===> 找不到c这个变量
const c = 10;
(3)同一作用域下let和const不能声明同名变量,而var可以
var a = 100;
console.log(a); // 100
var a = 10;
console.log(a); // 10
let a = 100;
let a = 10;
// 控制台报错:Identifier 'a' has already been declared ===> 标识符a已经被声明了。
(4)const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。但是这并不意味着不可变,如果声明的是复合类型数据,可以修改其属性
const a = 100; //声明后不能再修改
const list = [];
list[0] = 10;
console.log(list); // [10]
const obj = {a:100};
obj.name = 'apple';
obj.a = 10000;
console.log(obj); // {a:10000,name:'apple'}
(5)暂时性死区(temporal dead zone):只要块级作用域内存在let和const 命令,它所声明的变量就“定”(binding)这个区域,不再受外部的影响。
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
上面代码中,存在全局变量tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,所以在let声明变量前,对tmp赋值会报错。
ES6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
(6)用let和const声明的变量不会挂载到顶层对象
const a="aa";
console.log(window.a);// undefined
var b="bb";
console.log(window.b);// bb
window.a = 1;
var a = 2 ;
console.log(a); // 2
console.log(window.a); // 2
window.b = 1;
let b = 2 ;
console.log(b); // 2
console.log(window.b); // 1
六、VAR LET CONST的总结
(1)var定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问,不初始化出现undefined,不会报错。
(2)let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问
(3)const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改,如果声明的是复合类型数据,可以修改其属性
网友评论