解決ES5中只有全局做用域和函數做用域,沒有塊級做用域而帶來的不合理的場景。
複製代碼
用法和var 同樣,只是let聲明的變量只有在let命令所在的代碼塊有效前端
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
複製代碼
能夠看出var 聲明的變量在代碼塊以外也是能夠調用,而let聲明的則調用報錯。因此let 聲明只在它聲明的當前代碼塊中才能調用。面試
在使用 var 的時候會出現 「變量提高」的現象,即變量能夠在聲明以前使用,值爲undefined。let 改變了這種現狀,可是必須先聲明在使用,若是在聲明以前使用則會出現報錯。以下:數組
// var 的狀況
console.log(foo); // 輸出undefined
var foo = 2;
// let 的狀況
console.log(bar); // 報錯ReferenceError
let bar = 2;
複製代碼
只要塊級做用域內部存在 let 或者 const 命令,它所聲明的變量就「綁定」在這個區域,不會受外部影響。且暫時性死區的本質就是,只要一進入當前做用域,所要使用的變量就已經存在了,可是不可獲取,只有等到聲明變量的那一行代碼出現,才能夠獲取和使用該變量。以下:瀏覽器
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
複製代碼
ES6 明確規定,若是區塊中存在 let 和 const 命令,這個區塊對這些命令聲明的變量,從一開始就造成了封閉做用域。凡是在聲明以前就使用這些變量,就會報錯。安全
總之,在代碼塊內,使用let命令聲明變量以前,該變量都是不可用的。這在語法上,稱爲「暫時性死區」(temporal dead zone,簡稱 TDZ)。以下:bash
if (true) {
// TDZ開始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ結束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
複製代碼
++使用let聲明變量時,只要變量在尚未聲明完成前使用,就會報錯。上面這行就屬於這個狀況,在變量x的聲明語句尚未執行完成前,就去取x的值,致使報錯」x 未定義「。++數據結構
let 不容許在同一個做用域內聲明同一個變量,以下:函數
// 報錯
function func() {
let a = 10;
var a = 1;
}
// 報錯
function func() {
let a = 10;
let a = 1;
}
複製代碼
或者以下:ui
function func(arg) {
let arg;
}
func() // 報錯
function func(arg) {
{
let arg;
}
}
func() // 不報錯
複製代碼
上面也提到過在es5中沒有塊級做用域的概念,只有函數做用域和全局做用域,那麼就帶來了一些問題,以下:this
var tmp = new Date();
function f() {
console.log(tmp);
if (false) {
var tmp = 'hello world';
}
}
f(); // undefined
外層聲明被內層聲明所覆蓋,內層使用的是外層的聲明,內層變量提高致使 undefinded
複製代碼
var s = 'hello';
for (var i = 0; i < s.length; i++) {
console.log(s[i]);
}
console.log(i); // 5
常見的面試題,最後輸出不是預料中的 1 2 3 4 5 而所有是 5
複製代碼
function f1() {
let n = 5;
if (true) {
let n = 10;
}
console.log(n); // 5
}
內外層的 n 互不干擾
複製代碼
內外層能夠同名
{{{{
let insane = 'Hello World';
{let insane = 'Hello World'}
}}}};
或者
{{{{
{let insane = 'Hello World'}
console.log(insane); // 報錯
}}}};
複製代碼
塊級做用域的出現可讓如下當即執行函數的寫法沒必要要,以下:
// IIFE 寫法
(function () {
var tmp = ...;
...
}());
// 塊級做用域寫法
{
let tmp = ...;
...
}
複製代碼
在ES5中,函數只能在頂層做用域和函數做用域中聲明,不能在塊級做用域中聲明,可是瀏覽器爲了兼容性,仍是能夠在塊級做用域中聲明,理論上在ES6中 塊級做用域中聲明的函數,在外部調用會報錯,考慮環境的問題,應當避免在塊級做用域中聲明函數,若是須要也應當寫成函數表達式的方式,而不是函數聲明語句,以下:
// 函數聲明語句
{
let a = 'secret';
function f() {
return a;
}
}
// 函數表達式
{
let a = 'secret';
let f = function () {
return a;
};
}
複製代碼
const聲明的是一個常量 以下:
const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.
複製代碼
聲明以後若是在賦值,將會報錯,同時由於聲明的是常量,即const聲明後即要賦值否則也會報錯
const 和 let 相同,聲明也只在當前的塊級做用域生效。一樣也不會聲明提高,也存在暫時死區,只能在聲明以後使用,且和 let 同樣不得重複聲明,不能從新賦值。
const 所不能改變的並非值,而是變量指向的那個內存地址所保存的值不能變更,對於簡單類型(數值、字符串、布爾值),值就保存在變量所指向的內存地址中,所以等同於常量。而對於複合類型(數組、對象),變量指向的內存地址,保存的只是一個指向實際數據的指針,const 只能保證這個指針是固定的(即總指向一個固定的地址)。至於它指向的數據結構則是不能控制的 ,以下:
const foo = {};
// 爲 foo 添加一個屬性,能夠成功
foo.prop = 123;
foo.prop // 123
// 將 foo 指向另外一個對象,就會報錯
foo = {}; // TypeError: "foo" is read-only
常量foo儲存的是一個地址,這個地址指向一個對象。不可變的只是這個地址,
即不能把foo指向另外一個地址,但對象自己是可變的,因此依然能夠爲其添加新屬性。
複製代碼
或者
const a = [];
a.push('Hello'); // 可執行
a.length = 0; // 可執行
a = ['Dave']; // 報錯
常量a是一個數組,這個數組自己是可寫的,可是若是將另外一個數組賦值給a,就會報錯
複製代碼
const foo = Object.freeze({});
// 常規模式時,下面一行不起做用;
// 嚴格模式時,該行會報錯
foo.prop = 123;
對象也能夠凍結
var constantize = (obj) => {
Object.freeze(obj);
Object.keys(obj).forEach( (key, i) => {
if ( typeof obj[key] === 'object' ) {
constantize( obj[key] );
}
});
};
複製代碼
1. function
2. var
3. let
4. const
5. import
6. class
複製代碼
在瀏覽器環境指的是window對象,在 Node 指的是global對象,ES5 之中,頂層對象的屬性與全局變量是等價的。
window.a = 1;
a // 1
a = 2;
window.a // 2
複製代碼
ES5 的頂層對象,自己也是一個問題,由於它在各類實現裏面是不統一的。
同一段代碼爲了可以在各類環境,都能取到頂層對象,如今通常是使用this變量,可是有侷限性。
歡迎關注 公衆號【前端開發小白】