一愿卒、全局作用域
用 var 在全局(函數(shù)外)聲明的所有變量娘侍,都具有全局作用域向族,即: 網(wǎng)頁(yè)中所有腳本和函數(shù)均可使用吟策。
(1)最外層函數(shù)和在最外層函數(shù)外面定義的變量擁有全局作用域,例如:
var aName="哈哈";
function doSomething(){
var bName="嘿嘿";
function innerSay(){
alert(bName);
}
innerSay();
}
alert(aName); //哈哈
alert(bName); //腳本錯(cuò)誤
doSomething(); //嘿嘿
innerSay() //腳本錯(cuò)誤
(2)所有末定義直接賦值的變量自動(dòng)聲明為擁有全局作用域苟翻,例如:
function doSomething(){
var aName="哈哈";
bName="嘿嘿";
alert(aName);
}
doSomething(); //哈哈
alert(bName); //嘿嘿
alert(aName); //腳本錯(cuò)誤
二韵卤、局部作用域
局部作用域一般只在固定的代碼片段內(nèi)可訪問(wèn)到,最常見(jiàn)的例如函數(shù)內(nèi)部崇猫,例如下列代碼中的bName和函數(shù)innerSay都只擁有局部作用域沈条。在函數(shù)中用 var 聲明的所有變量,都是函數(shù)的局部變量诅炉,具有局部作用域蜡歹,即:變量只能在函數(shù)內(nèi)部使用,函數(shù)外部是不行的
function doSomething(){
var bName="嘿嘿";
function innerSay(){
alert(bName);
}
innerSay();
}
alert(bName); //腳本錯(cuò)誤
innerSay(); //腳本錯(cuò)誤
三涕烧、存在的問(wèn)題
在ES5中月而,只有全局作用域和函數(shù)作用域。這會(huì)導(dǎo)致函數(shù)作用域覆蓋了全局作用域议纯;亦或者循環(huán)中的變量泄露為全局變量父款。
(1)內(nèi)層變量可能覆蓋外層變量(變量提升)
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)用來(lái)計(jì)數(shù)的循環(huán)變量泄露為全局變量
for (var i = 0; i < 10; i++) {
console.log(i);
}
console.log(i); // 10
四瞻凤、塊級(jí)作用域
ES6中新增了塊級(jí)作用域憨攒,塊級(jí)作用域由 { } 包括,if語(yǔ)句和for語(yǔ)句里面的{ }也屬于塊級(jí)作用域阀参。ES6可以使用let關(guān)鍵字或者const關(guān)鍵字來(lái)實(shí)現(xiàn)塊級(jí)作用域肝集。let或const聲明的變量只在let或const命令所在的代碼塊{}內(nèi)有效,在{}之外不能訪問(wèn)蛛壳。
(1) 允許塊級(jí)作用域任意嵌套杏瞻,外層作用域無(wú)法讀取內(nèi)層作用域的變量
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) 內(nèi)層作用域可以定義外層作用域的同名變量
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
}
五所刀、塊級(jí)作用域之LET CONST
(1)ES6 新增了let和const命令,用來(lái)聲明變量伐憾。它的用法類似于var勉痴,但是所聲明的變量,只在let和const命令所在的代碼塊內(nèi)有效树肃。
if(1){
var a = 100;
let b = 10;
}
console.log(a); // 100
console.log(b) // 報(bào)錯(cuò):b is not defined ===> 找不到b這個(gè)變量
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命令改變了語(yǔ)法行為,它所聲明的變量一定要在聲明后使用胸嘴,否則報(bào)錯(cuò)雏掠。
console.log(a); // undefined ===> a已聲明還沒(méi)賦值,默認(rèn)得到undefined值
var a = 100;
console.log(b); // 報(bào)錯(cuò):b is not defined ===> 找不到b這個(gè)變量
let b = 10;
console.log(c); // 報(bào)錯(cuò):c is not defined ===> 找不到c這個(gè)變量
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;
// 控制臺(tái)報(bào)錯(cuò):Identifier 'a' has already been declared ===> 標(biāo)識(shí)符a已經(jīng)被聲明了乡话。
(4)const聲明的變量不得改變值,這意味著耳奕,const一旦聲明變量绑青,就必須立即初始化,不能留到以后賦值屋群。但是這并不意味著不可變闸婴,如果聲明的是復(fù)合類型數(shù)據(jù),可以修改其屬性
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)暫時(shí)性死區(qū)(temporal dead zone):只要塊級(jí)作用域內(nèi)存在let和const 命令芍躏,它所聲明的變量就“定”(binding)這個(gè)區(qū)域邪乍,不再受外部的影響。
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
上面代碼中对竣,存在全局變量tmp庇楞,但是塊級(jí)作用域內(nèi)let又聲明了一個(gè)局部變量tmp,導(dǎo)致后者綁定這個(gè)塊級(jí)作用域否纬,所以在let聲明變量前吕晌,對(duì)tmp賦值會(huì)報(bào)錯(cuò)。
ES6明確規(guī)定临燃,如果區(qū)塊中存在let和const命令聂使,這個(gè)區(qū)塊對(duì)這些命令聲明的變量,從一開始就形成了封閉作用域谬俄。凡是在聲明之前就使用這些變量,就會(huì)報(bào)錯(cuò)弃理。
if (true) {
// TDZ開始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ結(jié)束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
(6)用let和const聲明的變量不會(huì)掛載到頂層對(duì)象
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的總結(jié)
(1)var定義的變量,沒(méi)有塊的概念痘昌,可以跨塊訪問(wèn), 不能跨函數(shù)訪問(wèn)钥勋,不初始化出現(xiàn)undefined炬转,不會(huì)報(bào)錯(cuò)。
(2)let定義的變量算灸,只能在塊作用域里訪問(wèn)扼劈,不能跨塊訪問(wèn),也不能跨函數(shù)訪問(wèn)
(3)const用來(lái)定義常量菲驴,使用時(shí)必須初始化(即必須賦值)荐吵,只能在塊作用域里訪問(wèn),而且不能修改赊瞬,如果聲明的是復(fù)合類型數(shù)據(jù)先煎,可以修改其屬性