

最近在做畢業(yè)設(shè)計(jì),其中用到了一些 ES6 的語法,比如模塊的輸出引入,箭頭函數(shù),對象字面量的簡寫,等等。所性順便就學(xué)一下 ES6 的語法,做一個(gè)筆記總結(jié)。

let 和 const 命令
在 JavaScript 中變量作用域的基本單位總是function。如果你需要?jiǎng)?chuàng)建一個(gè)塊級作用域,除了普通的函數(shù)聲明以外最流行的方法就是使用立即被調(diào)用的函數(shù)表達(dá)式(IIFE)。
var a = 2;
(function IIFE(){
var a = 3;
? ? console.log( a );? // 3,a=3,只存在函數(shù)內(nèi)部
})();
console.log( a );? ? ? // 2
在ES6中實(shí)現(xiàn)了塊級作用域,不需要我們再去借用函數(shù)來實(shí)現(xiàn)塊級作用域了。
為了更加語義化,我們習(xí)慣用 {} 包裹let和const命令,用來表示塊級作用域的范圍
聲明變量用let,聲明常量用const;const用于那些你有意地并且明顯地標(biāo)識為不會改變的變量,具體使用什么應(yīng)該視情況而定
let命令
var a = 2;
{
? let a = 3;
? console.log( a ); // 3
}
console.log( a );? ? ? // 2
不存在變量提升,變量一定要在聲明后使用,否則會報(bào)錯(cuò),存在暫時(shí)性死區(qū)
塊級作用域,(使得廣泛應(yīng)用的立即執(zhí)行函數(shù)(IIFE)不在必要了
let不允許在相同的作用域內(nèi)重復(fù)聲明一個(gè)變量
const命令
聲明一個(gè)常量,一旦定義不允許修改
{
? const a = 2;
? console.log( a ); // 2
? a = 3;? ? ? ? ? ? ? ? // TypeError!,一旦在聲明時(shí)被設(shè)定就不允許你改變了
}
注意:const聲明的值不會因?yàn)閏onst而凍結(jié)或不可變,只是它的賦值被凍結(jié)了。如果這個(gè)值是一個(gè)復(fù)雜值,比如對象或數(shù)組,那么這個(gè)值的內(nèi)容仍然是可以被修改的(變量a實(shí)際上沒有持有一個(gè)固定的數(shù)組;而是指向數(shù)組的恒定的引用。(引用類型賦值)數(shù)組本身可以自由變化。):
{
? const a = [1,2,3];
? a.push( 4 );
? console.log( a );? ? // [1,2,3,4]
? a = 42;? ? ? ? ? ? ? ? ? // TypeError!
}
const 一旦聲明常量,就必須立即初始化,不能留到以后賦值,在聲明時(shí)被設(shè)定就不允許你改變了
只在聲明所在的塊級作用域內(nèi)有效,不能重復(fù)聲明常量
聲明的常量不提升,存在暫時(shí)性死區(qū),只能在聲明之后使用
const 不允許在相同的作用域內(nèi)重復(fù)聲明一個(gè)變量
對象數(shù)組的擴(kuò)散/收集
ES6引入了一個(gè)新的...操作符,它一般被稱作 擴(kuò)散(spread) 或 剩余(rest) 操作符
function foo(x,y,z) {
? console.log( x, y, z );
}
foo( ...[1,2,3] );? //1 2 3 將...[1,2,3] 擴(kuò)散成 x=1,y=2,z=3
var a = [2,3,4];
var b = [ 1, ...a, 5 ];
console.log( b ); //[1, 2, 3, 4, 5],相當(dāng)于[1].concat( a, [5] )
...操作符可把她們擴(kuò)散,同樣也可以把她們收集起來
function foo(...args) {
? console.log( args );
}
foo( 1, 2, 3, 4, 5); // [1,2,3,4,5],將1,2,3,4,5收集到...args里面。是一個(gè)參數(shù)數(shù)組
模板字符串
模板字符串是增強(qiáng)版的字符串,用反引號 ` 標(biāo)識,他可以當(dāng)做普通字符串使用,也可以用來定義多行字符串,或者在字符串中嵌入變量;
使用反引號 `? 將整個(gè)字符串包裹起來,${}包裹一個(gè)變量或者表達(dá)式,如果變量沒有聲明,則報(bào)錯(cuò),大括號中的值不是字符串,將按照一定的規(guī)則轉(zhuǎn)化為字符串
如果在字符串中需要使用反引號,則需要在其前面用反斜杠轉(zhuǎn)義(如 \ `);
靜態(tài)字符串一律使用單引號或反引號,不使用雙引號,動(dòng)態(tài)字符串使用反引號
function upper(s) {
? ? return s.toUpperCase();
}
let who = "reader";
let text =
`A very ${upper( "warm" )} welcome to all of you ${upper( `${who}s` )}!`;
console.log( text );
// A very WARM welcome to all of you READERS!
函數(shù)的擴(kuò)展
ES6允許為函數(shù)的參數(shù)設(shè)置默認(rèn)值,即直接寫在參數(shù)定義后面
function log(x,y='world'){
? console.log(x,y);
}
log('Hello');? //Hello world
參數(shù)變量是默認(rèn)聲明的,所以不能使用let和const再次聲明 ( 因?yàn)閘et和const不能重復(fù)聲明變量 )
通常情況下,定義了默認(rèn)值的參數(shù)應(yīng)該是函數(shù)的尾參數(shù),因?yàn)檫@樣比較容易看出,到底是省略了哪些參數(shù),如果非尾部的參數(shù)設(shè)置默認(rèn)值,實(shí)際上這個(gè)參數(shù)是無法省略的
ES6允許為函數(shù)的參數(shù)設(shè)置默認(rèn)值表達(dá)式,即直接把參數(shù)寫成表達(dá)式
function bar(val) {
? console.log( "bar called!" );
? return y + val;
}
function foo(x = y + 3, z = bar( x )) {
? console.log( x, z );
}
let y = 5;
foo();? // "bar called"? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 8 13
foo( 10 );? // "bar called"? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 10 15
注意:
let w = 1, z = 2;
function foo( x = w + 1, y = x + 1, z = z + 1 ) {
? console.log( x, y, z );
}
foo();? // ReferenceError,因?yàn)樵趨?shù)里面聲明了(他就沒向上尋找外部的作用域),而參數(shù)里面沒有初始化,所以直接使用會報(bào)錯(cuò)
對象字面量擴(kuò)展
一般情況的對象字面量聲明
let x=2,y=9,
? o={
? ? ? x:x,
? ? ? y:y
? },
? p={
? ? ? foo:function(){},
? ? ? bar:function(){}
? }
因?yàn)閷ο蟮膶傩悦妥兞棵恢?,在ES6中我們可以使用簡寫(省略:x,:y,:function):
let x=2,y=9,
? o={
? ? ? x,
? ? ? y
? },
? p={
? ? ? foo(){},
? ? ? bar(){}
? }
注意:我們應(yīng)當(dāng)僅在永遠(yuǎn)不需要將它們用于遞歸或事件綁定/解除時(shí)使用它們;否則會出現(xiàn)丟失函數(shù)的問題;
計(jì)算型屬性名
ES6為對象字面定義增加了一種語法,它允許你指定一個(gè)應(yīng)當(dāng)被計(jì)算的表達(dá)式,其結(jié)果就是被賦值屬性名。
let user='user_',
? ? o={
? ? ? ? [user+'foo']:function(){},
? ? ? ? [user+'bar']:function(){},
? ? };
console.log(o);? //Object {user_foo: function, user_bar: function}
箭頭函數(shù)
箭頭函數(shù)可以替換函數(shù)表達(dá)式,但是不能替換函數(shù)聲明;它們都是匿名函數(shù)表達(dá)式 —— 它們沒有可以用于遞歸或者事件綁定/解除的命名引用
如果箭頭函數(shù)的代碼部分多余一條語句,就要用大括號將其括起來,并使用return語句返回;
let foo = (x,y) => x + y;
let f3 = (x,y) => {
? ? let z = x * 2 + y;
? ? y++;
? ? x *= 3;
? ? return (x + y + z) / 2;
};
由于大括號被解析為代碼塊,所以如果箭頭函數(shù)直接返回一個(gè)對象,必須在對象外面加上括號
箭頭函數(shù)有幾個(gè)使用注意點(diǎn)
函數(shù)體內(nèi)的this對象就是定義時(shí)所在的對象,而不是使用時(shí)所在的對象;this對象的指向是固定的
不可以當(dāng)做構(gòu)造函數(shù),即不能使用new命令,否則會拋出一個(gè)錯(cuò)誤;因?yàn)榧^函數(shù)沒有自己的this,導(dǎo)致內(nèi)部的this就是外層代碼塊的this,所有沒有構(gòu)造函數(shù);
不可使用arguments對象,該對象在函數(shù)體內(nèi)不存在,如果要用,可以用rest參數(shù)替代
由于箭頭函數(shù)沒有自己的this,所以不能用call(),apply(),bind()這些方法去改變this的值;
在ES6中,默認(rèn)會用嚴(yán)格模式,因此this不會自動(dòng)指向window對象,而箭頭函數(shù)本身沒有this,因此this就只能是undefined;
簡單的,單行的,不會復(fù)用的函數(shù),建議采用箭頭函數(shù),如果函數(shù)體較為復(fù)雜,行數(shù)比較多,還是應(yīng)該使用傳統(tǒng)的函數(shù)寫法
尾調(diào)用優(yōu)化
函數(shù)調(diào)用會形成一個(gè)"調(diào)用記錄",又稱"調(diào)用幀",如果函數(shù)在函數(shù) A 的內(nèi)部調(diào)用函數(shù) B ,那么在 A 的調(diào)用幀上方形成一個(gè) B 的調(diào)用幀,等到 B 運(yùn)行結(jié)束,將結(jié)果返回 A ,B 的調(diào)用幀才會結(jié)束;
尾調(diào)用由于是函數(shù)的最后一步,不需要保留外層函數(shù)的調(diào)用幀,因?yàn)檎{(diào)用位置,內(nèi)部變量等信息都不會再用,直接內(nèi)層函數(shù)的調(diào)用幀取代外層函數(shù)即可;可以節(jié)省內(nèi)存
Module
模塊功能主要由兩個(gè)命令構(gòu)成import和export,并且他們都必須總是出現(xiàn)在它們分別被使用之處的頂層作用域。export命令用于規(guī)定模塊的對外接口,import命令用于輸入其他模塊提供的功能;在模塊中沒有全局作用域。
export命令
一個(gè)模塊就是一個(gè)獨(dú)立的文件,該文件內(nèi)部的所有變量,外部無法獲取,如果希望外部能夠讀取,模塊內(nèi)部的某個(gè)變量,就必須使用export關(guān)鍵字輸出該變量
export function foo() {
? ? // ..
}
import命令
使用export命令定義了模塊的對外接口以后,其他js文件可以通過import命令加載這個(gè)模塊文件
import foo form './foo';
表示加載foo.js文件,import命令接受一個(gè)對象(用大括號表示),里面指定要從其他模塊導(dǎo)入的變量名;大括號中變量名必須與被導(dǎo)入的模塊(foo.js)對外接口的名稱相同
import命令具有提升效果,會提升到整個(gè)模塊的頭部首先執(zhí)行;
如果在一個(gè)模塊中先后輸入后輸出同一個(gè)模塊,import和export語句寫在一起,不建議那樣寫;
module命令可以取代import語句,達(dá)到整體輸入模塊的作用;
module 命令后面跟一個(gè)變量,表示輸入的模塊定義在該變量上
export default命令為模塊指定默認(rèn)輸出,其他模塊在加載該模塊時(shí), 默認(rèn)輸出是一個(gè)函數(shù),import命令可以為該匿名函數(shù)指定任意名字;import命令后面不使用大括號;
export default命令只用于指定模塊的默認(rèn)輸出,顯然一個(gè)模塊只能有一個(gè)默認(rèn)輸出,因此export default命令只能使用一次,所以import命令后面才不加大括號,因?yàn)橹豢赡軐τ谝粋€(gè)方法
暫時(shí)就先介紹到這里;等畢設(shè)差不多做完的時(shí)候,就準(zhǔn)備著手找工作。
我要糾正我的一個(gè)小小的錯(cuò)誤,對象解構(gòu)不是ES6 的而是ES9的內(nèi)容,感謝 justjava 提出來啦,不然我又該去誤導(dǎo)別人啦!感謝 justjava,以后我會多關(guān)注一些相關(guān)的 proposal 。盡量不去犯一些常識性的錯(cuò)誤
變量的解構(gòu)賦值
從一個(gè)數(shù)組中取得索引的值,或從一個(gè)對象中取得屬性并手動(dòng)賦值可以被認(rèn)為是 結(jié)構(gòu)化賦值,比如
function foo() {
? return [1,2,3];
}
let tmp = foo(),
? ? a = tmp[0], b = tmp[1], c = tmp[2];
console.log( a, b, c );? ? ? ? ? ? // 1 2 3
如果剔除中間變量 tmp 而直接進(jìn)行賦值稱為解構(gòu)賦值
function foo() {
? return [1,2,3];
}
let [a,b,c]=foo();
console.log(a,b,c); // 1 2 3
從數(shù)組和對象中提取值,對變量進(jìn)行賦值,被稱為解構(gòu),本質(zhì)上屬于“ 模式匹配 ”,只要等號兩邊的模式相同,左邊的變量就會被賦予對應(yīng)的值
數(shù)組解構(gòu):
如果右邊的少于左邊的參數(shù),視為解構(gòu)不成功,變量值就等于 undefined
let [a,b,c,d]=['a'];
console.log(a,b,c,d);? //a undefined undefined undefined
如果右邊的多余于左邊的參數(shù),視為不完全解構(gòu),多余的值將會被忽略
let [a,b,c,d]=[1,2,3,4,5,6];
console.log(a,b,c,d);? //1 2 3 4
對象解構(gòu):
對象解構(gòu)與數(shù)組解構(gòu)有一個(gè)重要的不同,數(shù)組的元素是按照次序排列的,對象的屬性沒有次序,變量必須與屬性同名才能取到正確的值
let {bar,foo}={foo:'111',bar:'222'}
console.log(bar,foo);? //222 111
注意:上面我們使用了對象屬性賦值的簡寫,即如果屬性名與你想要聲明的變量名一致,我們可以使用簡寫,上面例子等同于
let {bar:bar,foo:bar}={foo:'111',bar:'222'}
console.log(bar,foo);? //222 111