### 1. 為什么需要塊級作用域?
ES5 只有全局作用域和函數作用域,沒有塊級作用域,這帶來很多不合理的場景。
第一種場景,內層變量可能會覆蓋外層變量。
~~~
var tmp = new Date();
function f() {
console.log(tmp);
if (false) {
var tmp = 'hello world';
}
}
f(); // undefined
~~~
上面代碼的原意是,`if`代碼塊的外部使用外層的`tmp`變量,內部使用內層的`tmp`變量。但是,函數`f`執行后,輸出結果為`undefined`,原因在于變量提升,導致內層的`tmp`變量覆蓋了外層的`tmp`變量。
第二種場景,用來計數的循環變量泄露為全局變量。
~~~
var s = 'hello';
for (var i = 0; i < s.length; i++) {
console.log(s[i]);
}
console.log(i); // 5
~~~
上面代碼中,變量i只用來控制循環,但是循環結束后,它并沒有消失,泄露成了全局變量。
### 2. ES6 的塊級作用域
let實際上為 JavaScript 新增了塊級作用域。
~~~
function f1() {
let n = 5;
if (true) {
let n = 10;
}
console.log(n); // 5
}
~~~
上面的函數有兩個代碼塊,都聲明了變量n,運行后輸出 5。這表示外層代碼塊不受內層代碼塊的影響。如果兩次都使用var定義變量n,最后輸出的值才是 10。
ES6 允許塊級作用域的任意嵌套。
`{{{{{let insane = 'Hello World'}}}}};`
上面代碼使用了一個五層的塊級作用域。外層作用域無法讀取內層作用域的變量。
~~~
{{{{
{let insane = 'Hello World'}
console.log(insane); // 報錯
}}}};
~~~
內層作用域可以定義外層作用域的同名變量。
~~~
{{{{
let insane = 'Hello World';
{let insane = 'Hello World'}
}}}};
~~~
塊級作用域的出現,實際上使得獲得廣泛應用的立即執行函數表達式(IIFE)不再必要了。
~~~
// IIFE 寫法
(function () {
var tmp = ...;
...
}());
// 塊級作用域寫法
{
let tmp = ...;
...
}
~~~
塊級作用域與函數聲明
函數能不能在塊級作用域之中聲明?這是一個相當令人混淆的問題。
ES5 規定,函數只能在頂層作用域和函數作用域之中聲明,不能在塊級作用域聲明。
~~~
// 情況一
if (true) {
function f() {}
}
// 情況二
try {
function f() {}
} catch(e) {
// ...
}
~~~
上面兩種函數聲明,根據 ES5 的規定都是非法的。
但是,瀏覽器沒有遵守這個規定,為了兼容以前的舊代碼,還是支持在塊級作用域之中聲明函數,因此上面兩種情況實際都能運行,不會報錯。
ES6 引入了塊級作用域,明確允許在塊級作用域之中聲明函數。ES6 規定,塊級作用域之中,函數聲明語句的行為類似于let,在塊級作用域之外不可引用。
~~~
function f() { console.log('I am outside!'); }
(function () {
if (false) {
// 重復聲明一次函數f
function f() { console.log('I am inside!'); }
}
f();
}());
~~~
上面代碼在 ES5 中運行,會得到“I am inside!”,因為在if內聲明的函數f會被提升到函數頭部,實際運行的代碼如下。
~~~
// ES5 環境
function f() { console.log('I am outside!'); }
(function () {
function f() { console.log('I am inside!'); }
if (false) {
}
f();
}());
~~~
ES6 就完全不一樣了,理論上會得到“I am outside!”。因為塊級作用域內聲明的函數類似于let,對作用域之外沒有影響。但是,如果你真的在 ES6 瀏覽器中運行一下上面的代碼,是會報錯的,這是為什么呢?
原來,如果改變了塊級作用域內聲明的函數的處理規則,顯然會對老代碼產生很大影響。為了減輕因此產生的不兼容問題,ES6 在附錄 B里面規定,瀏覽器的實現可以不遵守上面的規定,有自己的行為方式。
允許在塊級作用域內聲明函數。
函數聲明類似于var,即會提升到全局作用域或函數作用域的頭部。
同時,函數聲明還會提升到所在的塊級作用域的頭部。
注意,上面三條規則只對 ES6 的瀏覽器實現有效,其他環境的實現不用遵守,還是將塊級作用域的函數聲明當作let處理。
根據這三條規則,在瀏覽器的 ES6 環境中,塊級作用域內聲明的函數,行為類似于var聲明的變量。
~~~
// 瀏覽器的 ES6 環境
function f() { console.log('I am outside!'); }
(function () {
if (false) {
// 重復聲明一次函數f
function f() { console.log('I am inside!'); }
}
f();
}());
// Uncaught TypeError: f is not a function
~~~
上面的代碼在符合 ES6 的瀏覽器中,都會報錯,因為實際運行的是下面的代碼。
~~~
// 瀏覽器的 ES6 環境
function f() { console.log('I am outside!'); }
(function () {
var f = undefined;
if (false) {
function f() { console.log('I am inside!'); }
}
f();
}());
// Uncaught TypeError: f is not a function
~~~
> **考慮到環境導致的行為差異太大,應該避免在塊級作用域內聲明函數。如果確實需要,也應該寫成函數表達式,而不是函數聲明語句。***
~~~
// 函數聲明語句
{
let a = 'secret';
function f() {
return a;
}
}
// 函數表達式
{
let a = 'secret';
let f = function () {
return a;
};
}
~~~
另外,還有一個需要注意的地方。ES6 的塊級作用域允許聲明函數的規則,只在使用大括號的情況下成立,如果沒有使用大括號,就會報錯。
~~~
// 不報錯
'use strict';
if (true) {
function f() {}
}
// 報錯
'use strict';
if (true)
function f() {}
~~~
- js
- js繼承
- keyCode
- 好的網站
- 零散知識點-js
- This
- 對象深拷貝和淺拷貝
- 數組方法
- 數組的深拷貝和淺拷貝
- JS 引擎的執行機制
- js中的new
- 常用正則
- 函數柯里化
- 會修改當前數組的方法
- 不會修改當前數組的方法
- 函數式編程
- 循環遍歷
- 基礎知識
- 異步
- js知識總結
- fileReader
- HTML
- 零散知識點
- html5新特性
- viewport
- CSS
- cursor
- css3新特性
- 水平居中
- 垂直居中
- display解析
- 塊級元素和行內元素
- css技巧和方法
- 清除浮動
- Less
- Sass
- 綜合
- 微信小程序
- 前端面試
- CSS-面試
- JS-面試
- js-web-api
- js知識
- MVC-面試
- jQuery與框架的區別
- 閉包
- promise
- http狀態碼
- cdn
- 離線存儲
- 事件
- web安全
- 性能優化
- 響應式
- 服務器渲染和本地渲染
- 模板是什么?
- VUE流程
- 瀏覽器渲染過程
- this的指向
- new的使用
- HTML-面試
- title和alt區別
- html5元素
- h5新特性
- 圖片格式
- 零散面試總結
- react
- 生命周期-react
- state
- props
- 組件通信
- 虛擬DOM
- 源碼分析
- webstorm-template
- element與component區別
- 組件的理解
- JXS
- vue與react區別
- 16.8版本
- vue
- 生命周期-vue
- 實現流程
- webpack
- 概念
- 入口起點
- 出口
- loader
- 模式
- 插件
- manifest
- redux
- 介紹
- 核心概念
- 三大原則
- 基礎
- action
- reducer
- store
- 數據流
- 高級
- 異步action
- 異步數據流
- middleware
- ES6阮一峰
- ...
- let
- es6箭頭函數
- const
- 塊級作用域
- 頂層對象的屬性
- global 對象
- 變量的解構賦值
- 字符串的擴展
- promise對象
- 正則的擴展
- 數值的擴展
- Math對象的擴展
- 函數的擴展
- 數組的擴展
- 對象的擴展
- symbol
- async函數
- class的基本用法
- Class 的繼承
- Set 和 Map 數據結構
- 開發工具
- 好用的軟件
- chrome插件
- 其他實用工具
- 微信公眾號-前端早讀課
- 【第1352期】map和reduce,處理數據結構的利器
- 微信公眾號-前端大全
- JS 的執行機制
- 一篇文章理解 JS 繼承
- 瀏覽器
- 緩存
- 《Webkit技術內幕》之頁面渲染過程
- 跨域
- 安全
- XSS
- 設計模式
- 發布訂閱模式
- 工廠模式
- MV*模式
- 觀察者模式
- react-router
- 一些小技巧
- js一些小算法
- 1.已知一個數組中的值,在另外一個數組中查找該值
- 累加器
- 數組隨機
- 數組扁平化并去重排序
- Immutable
- 常用命令
- hybrid
- schema封裝
- typescript