let命令 學習筆記
1、let所聲明的變量,只在let
命令所在的代碼塊內有效。
2、不存在變量提升:所聲明的變量一定要在聲明后使用,否則報錯。
一定要先聲明,再去使用。let x=x;這樣就是錯誤的
ES6明確規定,如果區塊中存在let和const命令,這個區塊對這些命令聲明的變量,從一開始就形成了封閉作用域。凡是在聲明之前就使用這些變量,就會報錯。
3、不允許重復聲明:let不允許在相同作用域內,重復聲明同一個變量。即不能在函數內部重新聲明參數。
塊級作用域
1、為什么需要塊級作用域?
ES5 只有全局作用域和函數作用域,沒有塊級作用域,這帶來很多不合理的場景。
第一種場景,內層變量可能會覆蓋外層變量。
var tmp = new Date(); function f() { console.log(tmp); if (false) { var tmp = 'hello world'; } } f(); // undefined
第二種場景,用來計數的循環變量泄露為全局變量。下面代碼中,變量i只用來控制循環,但是循環結束后,它並沒有消失,泄露成了全局變量。
var s = 'hello'; for (var i = 0; i < s.length; i++) { console.log(s[i]); } console.log(i); // 5
2、ES6 的塊級作用域:外層作用域無法讀取內層作用域的變量。內層作用域可以定義外層作用域的同名變量。
下面的函數有兩個代碼塊,都聲明了變量n,運行后輸出5。這表示外層代碼塊不受內層代碼塊的影響。如果兩次都使用var定義變量n,最后輸出的值才是10。
function f1() { let n = 5; if (true) { let n = 10; console.log(n); } console.log(n); // 5 } f1()
塊級作用域的出現,實際上使得獲得廣泛應用的立即執行函數表達式(IIFE)不再必要了。
// IIFE 寫法 (function () { var tmp = ...; ... }()); // 塊級作用域寫法 { let tmp = ...; ... }
塊級作用域用法:考慮到環境導致的行為差異太大,應該避免在塊級作用域內聲明函數。如果確實需要,也應該寫成函數表達式,而不是函數聲明語句。
// 函數聲明語句 { let a = 'secret'; function f() { return a; } } // 函數表達式 { let a = 'secret'; let f = function () { return a; }; }
ES6 的塊級作用域允許聲明函數的規則,只在使用大括號的情況下成立,如果沒有使用大括號,就會報錯。
3、do 表達式
本質上,塊級作用域是一個語句,將多個操作封裝在一起,沒有返回值。想要解決方法:使得塊級作用域可以變為表達式,也就是說可以返回值,辦法就是在塊級作用域之前加上do,使它變為do表達式,然后就會返回內部最后執行的表達式的值。
let x = do { let t = f(); t * t + 1; };
const 命令
const
聲明一個只讀的常量。一旦聲明,常量的值就不能改變。const
一旦聲明變量,就必須立即初始化,不能留到以后賦值。
const的作用域與let命令相同:只在聲明所在的塊級作用域內有效。
const
命令聲明的常量也是不提升,同樣存在暫時性死區,只能在聲明的位置后面使用。
const
聲明的常量,也與let
一樣不可重復聲明。
箭頭函數
1、省略了function,花括號‘{}’用‘=>’代替了。這種寫法更簡潔了。
2、優點,就是函數體內的this的指向始終是指向定義它所在的對象,而不會指向調用它的對象,我們知道es5中的函數是誰執行它,它就指向誰。
ES6---數組array新增方法
參考:http://blog.csdn.net/wbiokr/article/details/65939582
Promise 對象
一、Promise 是異步編程的一種解決方案,比傳統的解決方案——回調函數和事件——更合理和更強大。Promise
對象是一個構造函數,用來生成Promise
實例.
補充知識:與 ‘new’ 運算符一起使用用來創建對象並初始化對象的‘函數’就是構造函數; 實例化(在面向對象的編程中,通常把用類創建對象的過程稱為實例化。)對象就是創建對象的過程!
var request = new XMLHttpRequest();
“new XMLHttpRequest();” 這句話就是一個標准的構造函數!我們 “var” 聲明了一個 “request” 對象,用構造函數 “new XMLHttpRequest();” 來初始化這個 “request” 對象為它賦初始值。
所謂Promise
,簡單說就是一個容器,里面保存着某個未來才會結束的事件(通常是一個異步操作)的結果。從語法上說,Promise 是一個對象,從它可以獲取異步操作的消息。Promise 提供統一的 API,各種異步操作都可以用同樣的方法進行處理。
二、Promise
對象有以下兩個特點。
(1)對象的狀態不受外界影響。Promise
對象代表一個異步操作,有三種狀態:pending
(進行中)、fulfilled
(已成功)和rejected
(已失敗)。只有異步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。
(2)一旦狀態改變,就不會再變,任何時候都可以得到這個結果。Promise
對象的狀態改變,只有兩種可能:從pending
變為fulfilled
和從pending
變為rejected
。只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果,這時就稱為 resolved(已定型)。如果改變已經發生了,你再對Promise
對象添加回調函數,也會立即得到這個結果。這與事件(Event)完全不同,事件的特點是,如果你錯過了它,再去監聽,是得不到結果的。
Promise
對象有缺點:
1,無法取消Promise
,一旦新建它就會立即執行,無法中途取消。
2,如果不設置回調函數,Promise
內部拋出的錯誤,不會反應到外部。
3,當處於pending
狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)。
下面代碼創造了一個Promise
實例。
var promise = new Promise(function(resolve, reject) { // ... some code if (/* 異步操作成功 */){ resolve(value); } else { reject(error); } });
Promise
構造函數接受一個函數作為參數,該函數的兩個參數分別是resolve
和reject
。它們是兩個函數,由 JavaScript 引擎提供,不用自己部署。
Promise
實例生成以后,可以用then
方法分別指定resolved
狀態和rejected
狀態的回調函數。
promise.then(function(value) { // success }, function(error) { // failure });
下面是一個Promise
對象的簡單例子。
function timeout(ms) { return new Promise((resolve, reject) => { setTimeout(resolve, ms, 'done'); }); } timeout(100).then((value) => { console.log(value); });
上面代碼中,timeout
方法返回一個Promise
實例,表示一段時間以后才會發生的結果。過了指定的時間(ms
參數)以后,Promise
實例的狀態變為resolved
,就會觸發then
方法綁定的回調函數。一般情況不調用setTimeout指定時間,Promise 新建后就會立即執行。
三、Promise.prototype.then()
then
方法返回的是一個新的Promise
實例(注意,不是原來那個Promise
實例)。因此可以采用鏈式寫法,即then
方法后面再調用另一個then
方法。
getJSON("/post/1.json").then(function(post) { return getJSON(post.commentURL); }).then(function funcA(comments) { console.log("resolved: ", comments); }, function funcB(err){ console.log("rejected: ", err); });
上面代碼中,第一個then
方法指定的回調函數,返回的是另一個Promise
對象。這時,第二個then
方法指定的回調函數,就會等待這個新的Promise
對象狀態發生變化。如果變為resolved
,就調用funcA
,如果狀態變為rejected
,就調用funcB
。如果采用箭頭函數,上面的代碼可以寫得更簡潔。
getJSON("/post/1.json").then( post => getJSON(post.commentURL) ).then( comments => console.log("resolved: ", comments), err => console.log("rejected: ", err) );
四、Promise.prototype.catch()
Promise.prototype.catch
方法是.then(null, rejection)
的別名,用於指定發生錯誤時的回調函數。
getJSON('/posts.json').then(function(posts) { // ... }).catch(function(error) { // 處理 getJSON 和 前一個回調函數運行時發生的錯誤 console.log('發生錯誤!', error); });
上面代碼中,getJSON
方法返回一個 Promise 對象,如果該對象狀態變為resolved
,則會調用then
方法指定的回調函數;如果異步操作拋出錯誤,狀態就會變為rejected
,就會調用catch
方法指定的回調函數,處理這個錯誤。另外,then
方法指定的回調函數,如果運行中拋出錯誤,也會被catch
方法捕獲。
getJSON('/post/1.json').then(function(post) { return getJSON(post.commentURL); }).then(function(comments) { // some code }).catch(function(error) { // 處理前面三個Promise產生的錯誤 });
Promise 對象的錯誤具有“冒泡”性質,會一直向后傳遞,直到被捕獲為止。也就是說,錯誤總是會被下一個catch
語句捕獲。
一般來說,不要在then
方法里面定義Reject狀態的回調函數(即then
的第二個參數),總是使用catch
方法。
// bad promise .then(function(data) { // success }, function(err) { // error }); // good promise .then(function(data) { //cb // success }) .catch(function(err) { // error });
上面代碼中,第二種寫法要好於第一種寫法,理由是第二種寫法可以捕獲前面then
方法執行中的錯誤,也更接近同步的寫法(try/catch
)。因此,建議總是使用catch
方法,而不使用then
方法的第二個參數。
const someAsyncThing = function() { return new Promise(function(resolve, reject) { // 下面一行會報錯,因為x沒有聲明 resolve(x + 2); }); }; someAsyncThing().then(function() { console.log('everything is great'); }); setTimeout(() => { console.log(123) }, 2000); // Uncaught (in promise) ReferenceError: x is not defined // 123
上面代碼中,someAsyncThing
函數產生的 Promise 對象,內部有語法錯誤。瀏覽器運行到這一行,會打印出錯誤提示ReferenceError: x is not defined
,但是不會退出進程、終止腳本執行,2秒之后還是會輸出123
。這就是說,Promise 內部的錯誤不會影響到 Promise 外部的代碼,通俗的說法就是“Promise 會吃掉錯誤”。
一般總是建議,Promise 對象后面要跟catch
方法,這樣可以處理 Promise 內部發生的錯誤。catch
方法返回的還是一個 Promise 對象,因此后面還可以接着調用then
方法。
五、Promise.all()
Promise.all
方法用於將多個 Promise 實例,包裝成一個新的 Promise 實例。
var p = Promise.all([p1, p2, p3]);
Promise.all
方法接受一個數組作為參數,p1
、p2
、p3
都是 Promise 實例,如果不是,就會先調用下面講到的Promise.resolve
方法,將參數轉為 Promise 實例,再進一步處理。
p的狀態由p1、p2、p3決定,分成兩種情況:
(1)只有p1、p2、p3的狀態都變成fulfilled,p的狀態才會變成fulfilled,此時p1、p2、p3的返回值組成一個數組,傳遞給p的回調函數。
(2)只要p1、p2、p3之中有一個被rejected,p的狀態就變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。
六、Promise.race()
Promise.race
方法同樣是將多個Promise實例,包裝成一個新的Promise實例。
var p = Promise.race([p1, p2, p3]); 上面代碼中,只要p1
、p2
、p3
之中有一個實例率先改變狀態,p
的狀態就跟着改變。那個率先改變的 Promise 實例的返回值,就傳遞給p
的回調函數。
七、Promise.resolve()
有時需要將現有對象轉為Promise對象,Promise.resolve
方法就起到這個作用。
八、模塊語法
1、ES6 在語言標准的層面上,實現了模塊功能,而且實現得相當簡單,完全可以取代 CommonJS 和 AMD 規范,成為瀏覽器和服務器通用的模塊解決方案。
ES6 模塊不是對象,而是通過export
命令顯式指定輸出的代碼,再通過import
命令輸入。
// ES6模塊 import { stat, exists, readFile } from 'fs';
上面代碼的實質是從fs
模塊加載3個方法,其他方法不加載。這種加載稱為“編譯時加載”或者靜態加載,即 ES6 可以在編譯時就完成模塊加載,效率要比 CommonJS 模塊的加載方式高。
2、嚴格模式
-
- 變量必須聲明后再使用
- 函數的參數不能有同名屬性,否則報錯
- 不能使用
with
語句 - 不能對只讀屬性賦值,否則報錯
- 不能使用前綴0表示八進制數,否則報錯
- 不能刪除不可刪除的屬性,否則報錯
- 不能刪除變量
delete prop
,會報錯,只能刪除屬性delete global[prop]
eval
不會在它的外層作用域引入變量eval
和arguments
不能被重新賦值arguments
不會自動反映函數參數的變化- 不能使用
arguments.callee
- 不能使用
arguments.caller
- 禁止
this
指向全局對象 - 不能使用
fn.caller
和fn.arguments
獲取函數調用的堆棧 - 增加了保留字(比如
protected
、static
和interface
)
尤其需要注意this的限制。ES6 模塊之中,頂層的this指向undefined,即不應該在頂層代碼使用this。
3、export 命令
模塊功能主要由兩個命令構成:export
和import
。export
命令用於規定模塊的對外接口,import
命令用於輸入其他模塊提供的功能。
一個模塊就是一個獨立的文件。該文件內部的所有變量,外部無法獲取。如果你希望外部能夠讀取模塊內部的某個變量,就必須使用export
關鍵字輸出該變量。下面是一個 JS 文件,里面使用export
命令輸出變量。
// profile.js var firstName = 'Michael'; var lastName = 'Jackson'; var year = 1958; export {firstName, lastName, year};
export
命令除了輸出變量,還可以輸出函數或類(class)。下面代碼對外輸出一個函數multiply
。
export function multiply(x, y) { return x * y; };
export
輸出的變量就是本來的名字,但是可以使用as
關鍵字重命名。
function v1() { ... } function v2() { ... } export { v1 as streamV1, v2 as streamV2, v2 as streamLatestVersion };
上面代碼使用as
關鍵字,重命名了函數v1
和v2
的對外接口。重命名后,v2
可以用不同的名字輸出兩次。需要特別注意的是,export
命令規定的是對外的接口,必須與模塊內部的變量建立一一對應關系。
// 寫法一 export var m = 1; // 寫法二 var m = 1; export {m}; // 寫法三 var n = 1; export {n as m};
上面三種寫法都是正確的,規定了對外的接口m
。其他腳本可以通過這個接口,取到值1
。它們的實質是,在接口名與模塊內部變量之間,建立了一一對應的關系。function
和class
的輸出,也必須遵守這樣的寫法。
另外,export
語句輸出的接口,與其對應的值是動態綁定關系,即通過該接口,可以取到模塊內部實時的值。
export var foo = 'bar'; setTimeout(() => foo = 'baz', 500);
上面代碼輸出變量foo
,值為bar
,500毫秒之后變成baz
。export
命令可以出現在模塊的任何位置,只要處於模塊頂層就可以。如果處於塊級作用域內,就會報錯
3、import 命令
使用export
命令定義了模塊的對外接口以后,其他 JS 文件就可以通過import
命令加載這個模塊。
// main.js import {firstName, lastName, year} from './profile'; function setName(element) { element.textContent = firstName + ' ' + lastName; }
import
命令接受一對大括號,里面指定要從其他模塊導入的變量名。大括號里面的變量名,必須與被導入模塊(profile.js
)對外接口的名稱相同。