儘管你可能連一行帶有塊級做用風格的代碼都沒有寫過,可是你這種常見的JavaScript代碼必定很熟悉: java
for (var i = 0; i < 5; i++) {
console.log(i);
}
複製代碼
咱們在for循環中直接定義了變量i,一般咱們只想在循環體內部的上下文環境中使用i,可是事情並非向着咱們但願的發展,i會被隱式的綁定到外面的做用域(函數做用域或者是全局做用域)。es6
var a = true;
if (a) {
var b = a * 2;
b = func(b);
console.log(b);
}
function func(b) {
return b + 1;
}
複製代碼
咱們聲明的變量b在代碼使用時,僅僅在if聲明的上下文使用,若是能將它限制在if的上下文將是一件頗有意義的事情,可是"理想很飽滿,如今很骨感",使用var聲明變量,它在任何地方都是同樣的,由於它將屬於外部做用域。。面試
上面簡單敘述了兩個小"梨子"編程
爲何咱們但願變量能夠綁定到本身的塊級做用域了,不急咱們慢慢往下看!bash
我相信在座的小夥子,若是每一天有一個面試官坐你對面:閉包
面試官:請簡單說一下塊級做用域。
你:(思考,首先想想什麼是塊級做用域....)???...不知道?
複製代碼
其實能夠拆分爲兩部分來解釋:做用域、塊級。編程語言
因此總結一下就是,塊級做用域就是包含在{...}中的做用域。在這個做用域中,擁有着和函數做用域相同的行爲。函數
就是你們一行ES6代碼都沒有寫過,可是你也可能知道,在包含let、const的代碼塊中存在一個塊級做用域。可是其實有不少種定義塊級做用域的方式。早在ES6以前就能夠建立塊級做用域。post
function m(obj) {
with(obj) {
a = 2;
console.log(a);
}
console.log(obj);
console.log(obj.a);
}
var obj = {}; m(obj);
複製代碼
with是一個難以理解的結構,JavaScript中有兩個機制能夠欺騙"詞法做用域的方式,with就是其中之一,with本質上經過將一個對象的引用當作做用域來處理,將對象的屬性當作做用域的標識符來處理,從而建立一個新的詞法做用域(運行時)。ui
try{
undefined();
} catch(err) {
console.log(err);
}
console.log(err);
複製代碼
到了你們都熟悉的ES6了。
var a = true;
if (a) {
let b = a * 2;
b = func(b);
console.log(b);
}
function func(b) {
return b + 1;
}
console.log(b);
複製代碼
這裏有一個小的知識可能須要你們注意,看以下代碼:
function f() {
console.log(a);
let a = 2;
}
f(); // ReferenceError: a is not defined
複製代碼
這段代碼直接報錯a is not defined,let和const擁有相似的特徵,阻止了變量提高,當執行console.log(a)的時候變量沒有定義
在MDN中認爲let不存在變量提高
這說明即便是 block 最後一行的 let 聲明,也會影響 block 的第一行。這就是提高(hoisting)
這句話也間接的證實 let hoisting 的存在。
那其實你們會有疑問,爲何上面的代碼會報錯。其實這並非因爲變量不提示致使的,而是因爲TDZ(臨時性死區)致使的。
{
a = 2;
let a;
}
複製代碼
{
let a;// 變量提高
"start TDZ"
a = 2; // 這裏在TDZ中間,因此會致使a = 2 報錯
a;
"end TDZ"
}
複製代碼
因此破案了:let是不存在變量提高。它「變量提高的行爲」,是因爲TDZ致使的。
so...總結一下
處了let之外,ES6還引入了const,一樣能夠用來建立塊級做用域變量,但其值是固定的(常量)。以後任何視圖修改的操做都會引發錯誤。
var tmp = new Date();
function f() {
console.log(tmp);
if (false) {
var tmp = 'hello world';
}
}
f(); // undefined
複製代碼
上面代碼的原意是,if代碼塊的外部使用外層的tmp變量,內部使用內層的tmp變量。可是,函數f執行後,輸出結果爲undefined,緣由在於變量提高,致使內層的tmp變量覆蓋了外層的tmp變量。
for (let i = 0; i < 5; i++) {
console.log(i);
}
console.log(i);
複製代碼
function func(obj) {
// doSomething
}
var obj = {...};
func(obj);
var bnt = document.getElementById('xxx');
bnt.addEventListener('click', function() {
// doSomething
});
複製代碼
在上述代碼中,點擊元素,觸發click事件,在這裏並不須要obj對象,理論上,當func執行後,在內存中obj就會被垃圾回收機制回收,可是click函數造成了一個覆蓋整個做用域的閉包。JavaScript引擎極有可能依然保持這個結構,而不進行回收。
function func(obj) {
// doSomething
}
{
let obj = {...};
func(obj);
}
var bnt = document.getElementById('xxx');
bnt.addEventListener('click', function() {
// doSomething
});
複製代碼
塊級做用域可讓引擎清楚的理解到沒有必要保持obj的內存,讓垃圾回收機制進行回收。
但願小夥伴喜歡個人文章,咱們一塊兒成長,謝謝你們!