ES6新特性
let關(guān)鍵字
控制作用域在塊級(jí)作用域下的定義關(guān)鍵字伍绳,并且不允許重復(fù)聲明搭伤,舉例:
for(var i = 0; i < 10; i++) {}
console.log(i);
// 輸出10
for(let j = 0; j < 10; j++) {}
console.log(j);
// j未定義只怎,報(bào)錯(cuò)(let定義的變量出了其所在代碼塊就銷(xiāo)毀)
注:
在沒(méi)有let
關(guān)鍵字之前,只用用var
定義變量怜俐,但由于其作用域在函數(shù)級(jí)別身堡,因此存在很多問(wèn)題,例如下面希望實(shí)現(xiàn)多個(gè)按鈕拍鲤,點(diǎn)擊輸出對(duì)應(yīng)的索引:
<body>
<button>a</button>
<button>b</button>
<button>c</button>
</body>
<script>
btns = document.getElementsByTagName('button');
for (var i = 0; i < btns.length; i++) {
// 使用var定義
btns[i].onclick = function() {
console.log(i);
}
}
</script>
結(jié)果發(fā)現(xiàn)不管點(diǎn)擊哪個(gè)按鈕贴谎,輸出的都是3,原因就是var
定義的變量i
作用域在函數(shù)擅这,因此這里的i
對(duì)于3個(gè)按鈕來(lái)說(shuō)是共用的,而這個(gè)i
經(jīng)過(guò)循環(huán)變成了3鲫构,所以無(wú)論哪個(gè)按鈕湿镀,獲得到的i
都是3算途。為了解決上述問(wèn)題,在ES6前提出了閉包的解決方案:
<body>
<button>a</button>
<button>b</button>
<button>c</button>
</body>
<script>
btns = document.getElementsByTagName('button');
for (var i = 0; i < btns.length; i++) {
// 通過(guò)閉包廓脆,即把i傳入到一個(gè)函數(shù)內(nèi)部停忿,避免變量被覆蓋
(function(i) {
btns[i].onclick = function() {
console.log(i);
}
})(i);
}
</script>
但是這樣需要多定義一個(gè)函數(shù),代碼上也顯得冗余颅停,因此ES6以后塊級(jí)作用域的let
關(guān)鍵字完美的解決了這個(gè)問(wèn)題:
<body>
<button>a</button>
<button>b</button>
<button>c</button>
</body>
<script>
btns = document.getElementsByTagName('button');
for (let i = 0; i < btns.length; i++) {
// 使用let定義塊級(jí)作用域變量溺欧,此時(shí)相當(dāng)于在3個(gè)代碼塊里分別let i = 0/1/2芥牌,三個(gè)i都在自己的作用域下,互相不會(huì)沖突
btns[i].onclick = function() {
console.log(i);
}
}
</script>
注2:
關(guān)于let是否存在變量提升問(wèn)題可以參考(事實(shí)上是存在的):https://blog.csdn.net/weixin_40169665/article/details/83819825
const關(guān)鍵字
定義常量宿礁,被定義的常量無(wú)法被更改梆靖,但是要注意定義常量的時(shí)候必須為其初始化,以及其作用域和let
一樣只在自己的代碼塊內(nèi)起作用返吻,并且也不可重復(fù)聲明。實(shí)際上const
相當(dāng)于一個(gè)不能被更改指向的指針街佑,但是如果指向的是對(duì)象這樣的數(shù)據(jù),那么對(duì)象的內(nèi)容是允許更改的捍靠,比如下面的操作是允許的:
const a = {name:1}
// 常量a指向一個(gè)對(duì)象
a["name"] = 2
// 修改對(duì)象的值允許
// const a = {name:1}
// 常量重新指向一個(gè)對(duì)象榨婆,此時(shí)不允許
數(shù)組解構(gòu)賦值
允許將數(shù)組的值分別賦值給多個(gè)變量烟央,舉例:
[x, y] = [1, 2]
// 此時(shí)x=1,y=2
[x, y] = [1, 2, 3, 4]
// 此時(shí)只有1和2被賦值給x和y,和上面的等價(jià)
[x, y, z] = [1, 2]
// z沒(méi)有被賦值怠硼,為undefined
對(duì)于數(shù)組當(dāng)中不希望賦值的數(shù)據(jù)鬼贱,可以用逗號(hào)間空開(kāi),舉例:
[a, , c] = [1, 2, 3]
// 此時(shí)a=1香璃,c=3这难,而2沒(méi)有賦值給任何一個(gè)數(shù)
如果希望把數(shù)組剩余的部分都賦值給某個(gè)變量,可以用...變量名
實(shí)現(xiàn)葡秒,舉例:
[a, ...c] = [1, 2, 3]
// 此時(shí)a=1姻乓,c=[2, 3]嵌溢,要注意...開(kāi)頭的變量必須放在最后
如果擔(dān)心數(shù)組的內(nèi)容不夠賦值給變量,還可以給變量設(shè)置默認(rèn)值蹋岩,舉例:
[a, b, c, d] = [1, 2, 3]
// 此時(shí)d沒(méi)有被賦值赖草,結(jié)果為undefined
[a, b, c, d=4] = [1, 2, 3]
// d設(shè)置了默認(rèn)值為4
對(duì)于字符串也可以當(dāng)做數(shù)組來(lái)解構(gòu),舉例:
[a, b, c] = 'xyz'
// a='x'剪个,...
這種解構(gòu)在函數(shù)傳參時(shí)也很實(shí)用秧骑,舉例:
function test([a, b, ...c]){
console.log(a + b, c);
}
test([1,2,3,4,5])
// 3, [3, 4, 5]
對(duì)象解構(gòu)賦值
對(duì)象的解構(gòu)賦值基于屬性名,舉例:
{a, b, c} = {a:1, b:2}
// a為1扣囊,b為2乎折,c沒(méi)有被賦值,為undefined
如果希望屬性賦值給變量名不同的變量侵歇,則看下面示例:
{a:A, b} = {a:1, b:2}
// 此時(shí)a的屬性賦值給變量A骂澄,所以變量a為未定義,A為1惕虑,b為2
由于對(duì)象解構(gòu)時(shí)是使用{}
包起來(lái)的坟冲,因此會(huì)被解析成代碼塊,此時(shí)就可能出現(xiàn)作用域沖突問(wèn)題枷遂,可以在外面包一層括號(hào)解決樱衷,舉例:
let x = 0;
({x} = {x:1})
對(duì)于多級(jí)對(duì)象的解構(gòu)示例如下:
{people: [{name}, age]} = {people:[{name:"aaa"}, 18]}
// 此時(shí)name為aaa,age為18
對(duì)象解構(gòu)的默認(rèn)值設(shè)置和數(shù)組的同理酒唉,舉例:
{x=0,y=0} = {x:1}
// 此時(shí)x為1矩桂,y為0
還有些用法如提取對(duì)象中的方法成變量調(diào)用,舉例:
let {floor} = Math;
// 將Math下的floor方法提取成變量
floor(1.5)
// 調(diào)用floor方法
let {length:s_length} = 'hello';
// 將字符串長(zhǎng)度屬性提取出來(lái)
s_length
// 輸出字符串長(zhǎng)度
對(duì)象解構(gòu)同樣可以用于函數(shù)傳參痪伦,且更方便侄榴,避免了傳遞順序等問(wèn)題,舉例:
function test({a, b}){
console.log(a + b);
}
test({a:1, b:2})
注:
這里順便介紹一些對(duì)象的基本用法网沾,比如直接定義函數(shù)癞蚕,舉例:
x = {a:1, b(){return 2;}, c(){console.log(3);}}
// {a: 1, b: ?, c: ?}
x.b()
// 2
x.c()
// 3
直接傳入已賦值的變量或常量,舉例:
let x = 1
const y = 2
z = {x, y}
// 將變量或常量傳入對(duì)象辉哥,此時(shí)z為:{x: 1, y: 2}
z.y = 3
// 但傳入以后都會(huì)變成變量桦山,所以z里的y可以修改
z
// {x: 2, y: 3}
設(shè)置默認(rèn)值,舉例:
function test({
x = 100,
y = 200
}) {
console.log(x, y);
}
test({x: 500, y: 200});
// 500 200
數(shù)組展開(kāi)
通過(guò)...
可以將數(shù)組展開(kāi)傳入醋旦,舉例:
function test(a, b, c) {
console.log(a + b + c);
}
test(...[1, 2, 3])
// 將數(shù)組展開(kāi)成1, 2, 3賦值給a, b, c
數(shù)組展開(kāi)還可以用于數(shù)組拼接恒水,舉例:
a = [1,2,3]
b = [4,5,6]
[...a, ...b]
// [1, 2, 3, 4, 5, 6]
實(shí)際上數(shù)組展開(kāi)就是把數(shù)組當(dāng)中的一個(gè)個(gè)數(shù)據(jù)通過(guò)類(lèi)似迭代器那樣給分離出來(lái),因此需要給一個(gè)數(shù)據(jù)類(lèi)型來(lái)存放所有分離出來(lái)的數(shù)據(jù)饲齐,舉例:
a = [1,2,3]
...a
// 報(bào)錯(cuò):VM323:1 Uncaught SyntaxError: Unexpected token ...钉凌,因?yàn)榉蛛x出來(lái)的數(shù)據(jù)沒(méi)有東西存放
[...a]
// 通過(guò)數(shù)組存放a中分離出來(lái)的數(shù)據(jù),結(jié)果為[1, 2, 3]
對(duì)象展開(kāi)
和數(shù)組類(lèi)似捂人,對(duì)象也能夠通過(guò)...
展開(kāi)御雕,同時(shí)也需要數(shù)據(jù)類(lèi)型來(lái)存放這個(gè)展開(kāi)后的所有數(shù)據(jù)矢沿,舉例:
a = {name: 111, pwd: 222}
{...a, b:1}
// 通過(guò)對(duì)象{b: 1}存放...a,結(jié)果為:{name: 111, pwd: 222, b: 1}
字符串新增方法
includes方法
判斷字符串是否存在于其中酸纲,舉例:
"abc".includes('a')
// true
"abc".includes('d')
// false
注:
該方法對(duì)數(shù)組同樣適用捣鲸,舉例:
[1,2,3].includes(1)
// true
startsWith方法
判斷字符串是否以其開(kāi)頭,舉例:
"abc".startsWith('a')
// true
"abc".startsWith('b')
// false
endsWith方法
判斷字符串是否以其結(jié)尾
repeat方法
將字符串復(fù)制成原來(lái)的幾倍闽坡,舉例:
"abc".repeat(3)
// "abcabcabc"
數(shù)組新增方法
map方法
對(duì)數(shù)組數(shù)據(jù)進(jìn)行統(tǒng)一操作摄狱,舉例:
a = [1,2,3,4,5]
a.map(function(item){return item>=3})
// 統(tǒng)一操作判斷每個(gè)數(shù)據(jù)是否大于等于3,[false, false, true, true, true]
// 由于上面的函數(shù)符合一個(gè)參數(shù)无午,且只有一條return語(yǔ)句,因此可以簡(jiǎn)寫(xiě)如下:
// 簡(jiǎn)寫(xiě)后:a.map(item=>item>=3)
// 這里容易看混掉的是第一個(gè)=>是代表箭頭函數(shù)祝谚,第二個(gè)=>代表大于等于運(yùn)算符
注:
map
方法里其實(shí)還有第二個(gè)參數(shù)宪迟,為數(shù)據(jù)索引,舉例:
a = [1,2,3,4,5]
a.map(function(item, index){return [item, index]})
// [[1,0],[2,1],[3,2],[4,3],[5,4]]
reduce方法
對(duì)整個(gè)數(shù)組進(jìn)行循環(huán)某運(yùn)算交惯,其下有三個(gè)參數(shù)為:tmp
/item
/index
次泽,分別代表上一次操作返回的結(jié)果(第一次默認(rèn)為數(shù)組的第一個(gè)數(shù))、下一個(gè)傳入的值(即數(shù)據(jù)的下一個(gè)數(shù))席爽、循環(huán)的索引意荤,舉例:
a = [1,2,3,4,5]
a.reduce(function(tmp, item, index) {
console.log(tmp, item);
return tmp + item;
// 返回結(jié)果為當(dāng)前數(shù)和下一個(gè)數(shù)的求和
})
// 最后的結(jié)果為15
filter方法
根據(jù)條件過(guò)濾數(shù)據(jù),舉例:
a = [1,2,3,4,5]
a.filter(item => item%2==0)
// 返回所有偶數(shù)只锻,結(jié)果為[2, 4]
forEach方法
遍歷數(shù)組玖像,舉例:
a = [1,2,3,4,5]
a.forEach((item, index) => {console.log(item, index)})
// 循環(huán)輸出:1 0、2 1齐饮、...
some方法
循環(huán)判斷數(shù)組中是否存在滿(mǎn)足條件的數(shù)據(jù)捐寥,只要有一個(gè)滿(mǎn)足就返回true
,舉例:
a = [1,2,3,4,5]
a.some(item=>item>=3)
// 有大于等于3的數(shù)祖驱,返回true
every方法
和some
方法類(lèi)似握恳,循環(huán)判斷數(shù)組中是否全部滿(mǎn)足條件,是則返回true
捺僻,舉例:
a = [1,2,3,4,5]
a.every(item=>item>=3)
// 有不大于等于3的數(shù)乡洼,返回false
對(duì)象新增方法
Object.keys()
會(huì)將對(duì)象的所有鍵以數(shù)組形式輸出,舉例:
o = {a:1, b:2, c:3}
Object.keys(o)
// ["a", "b", "c"]
Object.values()
會(huì)將對(duì)象的所有值以數(shù)組形式輸出
Object.entries()
會(huì)將對(duì)象的所有鍵和值以數(shù)組形式輸出匕坯,數(shù)組中的子元素是長(zhǎng)為2的數(shù)字束昵,分別為鍵和值,舉例:
o = {a:1, b:2, c:3}
Object.entries(o)
// [Array(2), Array(2), Array(2)]
Object.entries(o)[0]
// ["a", 1]
模板字符串
原來(lái)的字符串要拼接值都是通過(guò)多個(gè)+
號(hào)實(shí)現(xiàn)醒颖,而通過(guò)反引號(hào)包起來(lái)的模板字符串則可以通過(guò)${value}
傳值妻怎,舉例:
x = 2
`1${x}3`
// 123
模板字符串還可以嵌套,舉例:
`1${x+`${y}`}3`
// 1203
標(biāo)簽?zāi)0?/h5>
在定義模板字符串時(shí)泞歉,可以使用標(biāo)簽?zāi)0鍖?duì)其進(jìn)行一些操作逼侦,舉例:
function toLower(strings, ...params) {
// 傳入第一個(gè)參數(shù)為模板字符串中的靜態(tài)字符匿辩,params接收多個(gè)傳入的變量
console.log(strings, params);
let s = "";
strings.forEach((item, index) => {
if (index >= params.length) return;
s += item + params[index].toLowerCase();
})
s += strings.slice(-1);
return s;
}
let name = "Aaa";
let result = toLower`My name is ${name}.`;
console.log(result);
// [ 'My name is ', '.' ] [ 'Aaa' ]
// My name is aaa.
Symbol數(shù)據(jù)類(lèi)型
生成一個(gè)每次都不同的隨機(jī)變量,一般用于防止對(duì)象屬性被修改榛丢,舉例:
a = Symbol()
b = Symbol()
a == b
// false
c = {}
c[a] = 1
c[b] = 2
c
// {Symbol(): 1, Symbol(): 2}铲球,可以看出a的值沒(méi)有被b給覆蓋
Set類(lèi)型
集合,舉例:
s = new Set([1,2,3,3])
// Set(3) {1, 2, 3}晰赞,可以看出結(jié)果為3個(gè)
s.size
// 3稼病,相當(dāng)于length
s.add(4)
// Set(4) {1, 2, 3, 4},添加了4進(jìn)去
s.delete(5)
// false掖鱼,沒(méi)有5然走,刪除失敗
s.delete(3)
// true,刪除成功
s.has(2)
// true戏挡,存在2
s.clear()
// 清空集合
迭代器
可以惰性加載數(shù)據(jù)芍瑞,并且每個(gè)數(shù)據(jù)只能加載一次,不能回退
自定義迭代器實(shí)現(xiàn)
function createIterator(arr) {
let i = 0;
let done = false;
return {
next() {
if (i === arr.length) done = true;
return {value: arr[i++], done};
}
}
}
let iterator = createIterator([1,2,3]);
while ((item = iterator.next()) && (!item.done)) {
console.log(item);
}
// { value: 1, done: false }
// { value: 2, done: false }
// { value: 3, done: false }
通過(guò)Symbol實(shí)現(xiàn)迭代器
let arr = [1, 2, 3];
let createIterator = arr[Symbol.iterator];
// 創(chuàng)建迭代器函數(shù)
let iterator = createIterator.call(arr);
// 綁定迭代器數(shù)據(jù)
while ((item = iterator.next()) && !item.done) {
console.log(item);
}
// { value: 1, done: false }
// { value: 2, done: false }
// { value: 3, done: false }
生成器
在函數(shù)名前加符號(hào)*
褐墅,并且通過(guò)yield
關(guān)鍵字返回值拆檬,通過(guò)next
方法獲取下一個(gè)的值,舉例:
function *gen() {
yield 1
yield 2
yield 3
return 100
}
let g = gen();
console.log(g.next());
console.log(g.next());
console.log(g.next());
console.log(g.next());
console.log(g.next());
// { value: 1, done: false }
// { value: 2, done: false }
// { value: 3, done: false }
// { value: 100, done: true }
// { value: undefined, done: true }
生成器特性
生成器相當(dāng)于可以暫停的函數(shù)妥凳,因此我們可以在函數(shù)上一次返回的地方繼續(xù)執(zhí)行竟贯,舉例:
// 無(wú)限生成的遞增數(shù)字
function *gen() {
let i = 0;
while (true) {
yield i++;
}
}
let g = gen();
console.log(g.next());
console.log(g.next());
console.log(g.next());
console.log(g.next());
console.log(g.next());
// { value: 0, done: false }
// { value: 1, done: false }
// { value: 2, done: false }
// { value: 3, done: false }
// { value: 4, done: false }
函數(shù)參數(shù)默認(rèn)值
函數(shù)支持給參數(shù)設(shè)置默認(rèn)值,舉例:
function test(x, y=1){
return x + y;
}
test(1);
// 2
test(1, 2);
// 3
常量函數(shù)命名
可以通過(guò)定義一個(gè)常量而不用function
關(guān)鍵字來(lái)命令一個(gè)函數(shù)逝钥,舉例:
const FUN = "test";
// 定義一個(gè)常量FUN屑那,值為test
funs = {
[FUN] (x, y) {
return x + y;
}
}
// 定義以后,可以發(fā)現(xiàn)funs為:{test: ?}艘款,即定義了一個(gè)名為test的函數(shù)
funs.test(1, 2)
// 3
不過(guò)本人嘗試以后發(fā)現(xiàn)貌似不用常量齐莲,用字符串也可以這樣定義(常量傳進(jìn)對(duì)象本身也都不是常量了)
箭頭函數(shù)
能夠?qū)崿F(xiàn)對(duì)函數(shù)的簡(jiǎn)寫(xiě),舉例:
function(x, y) {
...
}
// 上面的原來(lái)函數(shù)的寫(xiě)法
(x, y) => { ... }
// 通過(guò)箭頭函數(shù)可以簡(jiǎn)寫(xiě)并替代上面的寫(xiě)法
其中使用箭頭函數(shù)有以下注意事項(xiàng):
- 當(dāng)參數(shù)只有一個(gè)時(shí)磷箕,可以省略
()
选酗,舉例:
function(data) {
console.log(data);
}
// 只有一個(gè)參數(shù)
data => {console.log(data);}
// 省略了括號(hào)
- 如果函數(shù)里只有一個(gè)
return
語(yǔ)句,那么可以不用加{}
岳枷,并且可以省略return
關(guān)鍵字芒填,舉例:
function(x, y) {
return x + y;
}
// 只有一個(gè)return語(yǔ)句
(x, y) => x + y;
// 省略了花括號(hào)和return關(guān)鍵字
注:
通過(guò)箭頭函數(shù)還能夠綁定this
,使得this
一直為當(dāng)前的環(huán)境而不會(huì)被變更空繁,舉例:
a = {
name: "aaa",
fn: function() {
console.log(this, this.name + "bbb")
// 使用function定義的this會(huì)綁定到當(dāng)前對(duì)象殿衰,因此輸出為當(dāng)前對(duì)象a和"aaabbb"
},
fn1: () => {
console.log(this, this.name + "bbb")
// 箭頭函數(shù)使得this綁定在window對(duì)象,因此輸出為window對(duì)象和"bbb"
}
}
a.fn()
a.fn1()
JSON操作
對(duì)象轉(zhuǎn)json
通過(guò)JSON
對(duì)象的stringify
方法實(shí)現(xiàn)盛泡,舉例:
JSON.stringify({a:1, b:2})
// "{"a":1,"b":2}"
json轉(zhuǎn)對(duì)象
通過(guò)JSON
對(duì)象的parse
方法實(shí)現(xiàn)闷祥,舉例:
JSON.parse('{"a":1,"b":2}')
// {a: 1, b: 2}
面向?qū)ο?/h5>
ES6之前JS的面向?qū)ο笫且粋€(gè)偽面向?qū)ο螅嬖谥S多問(wèn)題傲诵,比如用function
關(guān)鍵字定義類(lèi)凯砍,以至于不知道是函數(shù)還是類(lèi)箱硕、添加方法方式限制不嚴(yán),導(dǎo)致代碼編寫(xiě)可能不統(tǒng)一等等一系列問(wèn)題悟衩。因此在ES6中提供了幾個(gè)面向?qū)ο蟮年P(guān)鍵字統(tǒng)一了JS的面向?qū)ο缶幊叹缯郑ǎ?code>class(定義類(lèi))、constructor
(構(gòu)造器)座泳、extends
(繼承)惠昔、super
(父類(lèi)),static
(定義靜態(tài)屬性/方法)舉例:
class User {
constructor(name, pwd) {
// 構(gòu)造方法
this.name = name;
this.pwd = pwd;
}
showName() {
// 直接輸入方法名定義方法
console.log(this.name);
}
static showRule() {
// 定義靜態(tài)方法挑势,可以從類(lèi)直接調(diào)用而無(wú)需實(shí)例化
console.log("this is User rule");
}
static rule = "this is rule";
// 定義靜態(tài)屬性镇防,如果報(bào)錯(cuò)可能是瀏覽器版本不支持,建議換成最新chrome嘗試
}
class Vip extends User {
// 繼承父類(lèi)
constructor(name, pwd, level) {
super(name, pwd);
// 調(diào)用父類(lèi)構(gòu)造方法
this.level = level;
}
showPwd() {
console.log(this.pwd);
}
showLevel() {
console.log(this.level);
}
}
console.log(User.rule);
User.showRule();
// 直接從類(lèi)調(diào)用靜態(tài)方法
let user = new Vip("aaa", "111", "1");
user.showName();
user.showPwd();
user.showLevel();
類(lèi)表達(dá)式
- 我們可以通過(guò)類(lèi)表達(dá)式來(lái)定義匿名類(lèi)潮饱,舉例:
const test = class Test {
constructor() {
console.log("init test");
}
}
new test();
- 類(lèi)表達(dá)式可以省略類(lèi)名:
const test = class {
constructor() {
console.log("init test");
}
}
new test();
- 可以通過(guò)該方式創(chuàng)建內(nèi)部類(lèi):
class Base {}
function Factory(cls) {
return class extends Base {
test() {
console.log(cls);
}
}
}
const cls = Factory("test")
const instance = new cls();
instance.test();
fetch
fetch
是一個(gè)新提供的網(wǎng)絡(luò)請(qǐng)求api营罢,在fetch
出來(lái)之前,異步請(qǐng)求主要還是用ajax
和axios
(axios
相當(dāng)于結(jié)合ajax
和promise
后再封裝了一層)饼齿。fetch
也是基于promise
管理(因此可以像promise
對(duì)象那樣處理),并且是JS原生的內(nèi)置類(lèi)蝙搔,所以可以直接使用缕溉,而無(wú)需像ajax
和axios
那樣還需要先導(dǎo)入一個(gè)庫(kù)。并且fetch
底層不是基于XMLHttpRequest
實(shí)現(xiàn)的吃型,官方的意思是用了一種更好的方案實(shí)現(xiàn)证鸥,不過(guò)fetch
不支持老版本IE,并且fetch
和原來(lái)的ajax
勤晚、axios
不一樣枉层,他只要有服務(wù)端返回就算訪問(wèn)成功(別的會(huì)根據(jù)狀態(tài)碼等來(lái)判斷),因此只有網(wǎng)絡(luò)連接失敗時(shí)才算是訪問(wèn)失敗赐写。
使用
fetch
的使用十分簡(jiǎn)單鸟蜡,只需要配置第一個(gè)參數(shù)url
,以及第二個(gè)可選參數(shù)(請(qǐng)求的配置和傳輸?shù)臄?shù)據(jù)對(duì)象等)挺邀,使用舉例:
fetch("http://jsonplaceholder.typicode.com/users").then(res => res.json()).then(json => {console.log(json)})
// 向該網(wǎng)址發(fā)送請(qǐng)求,并且第二個(gè)參數(shù)不傳默認(rèn)為get請(qǐng)求端铛。
fetch("http://xxx", {
method: "post",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({'name': 'aaa'})
})
.then(res => res.json())
.then(json => {
console.log(json);
});
// 指定發(fā)送的數(shù)據(jù)格式和內(nèi)容等
更多參考:
https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch
https://blog.csdn.net/qq_36754767/article/details/89645041
模塊化
使用步驟
1.通過(guò)export
導(dǎo)出成員
2.在另一個(gè)文件當(dāng)中通過(guò)import
接收成員
3.引用通過(guò)webpack
編譯后生成的bundle.js
文件
舉例:
- a.js
export let a = { name: "aaa" };
export const b = 111;
// 導(dǎo)出成員a和b
- b.js
import * as a from './a'
// 導(dǎo)入a.js里的所有成員泣矛,注意相對(duì)路徑需要加`./`這樣的定位符
console.log(a.a, a.b);
// 輸出導(dǎo)入的成員
- webpack.config.js
module.exports = {
mode: 'development',
entry: './b.js',
output: {
filename: 'bundle.js',
path: __dirname + '/build'
}
}
導(dǎo)入webpack生成的文件后可以發(fā)現(xiàn)輸出了a
和b
的值。
注:
注意相對(duì)路徑需要加./
這樣的定位符禾蚕,因?yàn)槟K化需要在node環(huán)境下使用您朽,如果不加定位符,則默認(rèn)去依賴(lài)模塊當(dāng)中尋找换淆,而不會(huì)去相對(duì)路徑下找
默認(rèn)成員default
在模塊化當(dāng)中還可以導(dǎo)出默認(rèn)成員default
哗总,則通過(guò)導(dǎo)入模塊成功語(yǔ)法時(shí)几颜,只導(dǎo)入默認(rèn)成員default
的值,舉例:
- a.js
export default "aaa";
// 導(dǎo)出默認(rèn)成員"aaa"
- b.js
import a from './a'
// 導(dǎo)入默認(rèn)成員default魂奥,并命名為a菠剩,相當(dāng)于下面這個(gè)意思
// import default as a from './a'
console.log(a);
這種語(yǔ)法在vue
的組件化工程當(dāng)中很常見(jiàn)
導(dǎo)入成員語(yǔ)法
前面介紹了兩種語(yǔ)法,一種是導(dǎo)入全部:
import * as xxx from './xxx'
還有導(dǎo)入默認(rèn)默認(rèn)成員語(yǔ)法:
import xxx from './xxx'
如果希望導(dǎo)入指定的成員也可以:
import {xxx, yyy as zzz} from './xxx'
// 導(dǎo)入./xxx文件里的xxx和yyy成員耻煤,并將yyy改名為zzz
還有導(dǎo)入如CSS文件具壮、圖片文件等模塊時(shí)(在模塊化開(kāi)發(fā)下一些靜態(tài)文件都稱(chēng)為模塊),因?yàn)檫@些文件里可能不存在成員啥的哈蝇,所以可以只導(dǎo)入棺妓,舉例:
import './xxx'
還有異步引入,使用import
方法炮赦,舉例:
let x = import('./xxx')
// 當(dāng)需要用到的時(shí)候引入怜跑,簡(jiǎn)化代碼體積
// 傳回來(lái)的是個(gè)Promise對(duì)象,需要使用await關(guān)鍵字等待
模塊化導(dǎo)入路徑問(wèn)題
-
'xxx'
:直接從npm依賴(lài)?yán)飳ふ蚁嚓P(guān)模塊 -
'./xxx'
/../xxx
:從相對(duì)路徑當(dāng)中尋找相關(guān)模塊 -
@/xxx
:從src目錄下尋找相關(guān)模塊
瀏覽器兼容
對(duì)于ES6以上的寫(xiě)法吠勘,在一個(gè)古老的瀏覽器上是不兼容的性芬,因此需要對(duì)這些內(nèi)容進(jìn)行編譯
babel.js編譯
需要安裝node環(huán)境
安裝
npm install @babel/core @babel/cli @babel/preset-env -D
使用步驟
1.初始化項(xiàng)目環(huán)境:npm init -y
2.在安裝好babel后,在項(xiàng)目下添加一個(gè)文件.babelrc
剧防,添加內(nèi)容如下:
{
presets: ["@babel/preset-env"]
}
3.在src
目錄下編寫(xiě)js文件
4.輸入命令:babel src -d dest
植锉,即可在dest
目錄發(fā)現(xiàn)文件被編譯成兼容低版本瀏覽器的js文件了
注:
如果嫌命令麻煩,也可以打開(kāi)package.json
文件峭拘,在script
屬性下配置自定義命令:"自定義命令": "babel src -d dest"
俊庇,此時(shí)只需要輸入命令:npm run 自定義命令
即可
ES7新特性
冪操作
通過(guò)**
可以實(shí)現(xiàn)冪運(yùn)算,舉例:
2**3
// 8
(感覺(jué)這越發(fā)展越來(lái)越像java和python的結(jié)合體了)
ES8新特性
異步操作控制
對(duì)于異步操作(如ajax)本身帶來(lái)了很大的便利鸡挠,但是由于其異步的關(guān)系辉饱,有些依賴(lài)性的異步代碼則很可能陷入一種回調(diào)地獄,比如下面的代碼:
$.ajax({
url: "https://xxx",
success(data) {
// 當(dāng)訪問(wèn)xxx成功時(shí)訪問(wèn)yyy
$.ajax({
url: "https://yyy",
success(data) {
// 當(dāng)訪問(wèn)yyy成功時(shí)訪問(wèn)zzz
$.ajax({
url: "https://zzz",
success(data) {
// 一直回調(diào)下去...
},
error() {}
})
},
error() {}
})
},
error() {}
})
上面的代碼里對(duì)于yyy的訪問(wèn)是基于xxx訪問(wèn)成功的前提下拣展,而zzz的訪問(wèn)是基于yyy訪問(wèn)成功的前提下彭沼,一直這樣下去,代碼會(huì)十分混亂备埃,所以為了美化這種代碼溜腐,提出了兩種解決方案:
- Promise對(duì)象(其實(shí)是ES5.5的內(nèi)容)
- async/await關(guān)鍵字
Promise對(duì)象
用于封裝異步操作,使用步驟如下:
1.實(shí)例化一個(gè)Promise()
對(duì)象
2.將所有異步操作封裝到Promise()
對(duì)象內(nèi)部瓜喇,里面?zhèn)魅雰蓚€(gè)參數(shù)挺益,分別是執(zhí)行成功和失敗的回調(diào)函數(shù)
3.通過(guò)Promise()
對(duì)象的then()
方法調(diào)用異步操作時(shí)執(zhí)行的成功和失敗的回調(diào)函數(shù)
舉例:
let p = new Promise(function(resolve, reject) {
// 封裝異步操作,傳入成功時(shí)執(zhí)行的resolve函數(shù)和失敗執(zhí)行的reject函數(shù)
$.ajax({
url: "https://xxx",
success(data) {
resolve("success", data);
// 成功的話執(zhí)行resolve函數(shù)乘寒,并傳入一個(gè)狀態(tài)和對(duì)應(yīng)的數(shù)據(jù)望众,在后面then的時(shí)候調(diào)用
},
error(data) {
reject("error", data);
// 失敗執(zhí)行reject函數(shù),在后面catch的時(shí)候調(diào)用
}
});
});
// 在這里進(jìn)行異步操作的回調(diào)處理
p.then(function(status, data) {
// 成功時(shí)的回調(diào)函數(shù),相當(dāng)于此時(shí)執(zhí)行resolve方法
console.log(status, ":", data);
}).catch(function(status, data) {
// 失敗時(shí)的回調(diào)函數(shù)烂翰,相當(dāng)于此時(shí)執(zhí)行reject方法
console.log(status, ":", data);
});
從上面代碼可以看出夯缺,此時(shí)整個(gè)異步操作被放在了一個(gè)對(duì)象p里,然后后續(xù)只需要調(diào)用p.then().catch()
來(lái)進(jìn)行異步操作的回調(diào)即可甘耿,相比之前的來(lái)說(shuō)踊兜,這個(gè)顯然看起來(lái)更加直觀
Promise統(tǒng)一封裝
Promise對(duì)象下有一個(gè)all
方法,可以將所有的異步操作通過(guò)數(shù)組方式傳入佳恬,然后當(dāng)所有異步操作都執(zhí)行成功或存在失敗操作時(shí)通過(guò)then
方法調(diào)用后續(xù)操作捏境,舉例:
new Promise.all([
// 傳入一個(gè)包含多個(gè)異步操作的數(shù)組
$.ajax({ url: "https://xxx", dataType: "json"}),
$.ajax({ url: "https://yyy", dataType: "json"}),
$.ajax({ url: "https://zzz", dataType: "json"}),
]).then(
// 通過(guò)then方法調(diào)用后續(xù)操作,分別傳入一個(gè)成功和失敗的處理方法
([data1, data2, data3]) => {
// 返回的數(shù)組分別是data1,data2,data3毁葱,通過(guò)結(jié)構(gòu)方式接受
console.log(data1, data2, data3);
}, res => {
console.log("失敗");
})
Promise異步擇優(yōu)
Promise下有race
方法和all
方法操作類(lèi)似垫言,也是傳入一個(gè)異步操作數(shù)組,然后當(dāng)最快的一個(gè)異步操作完成時(shí)則調(diào)用then
方法進(jìn)行后續(xù)操作倾剿,并忽視其他的異步操作聋袋,若所有操作都失敗才會(huì)執(zhí)行錯(cuò)誤方法凝垛,常用于類(lèi)似CDN加速時(shí)選擇最快的鏈接場(chǎng)景截珍,舉例:
Promise.race([
$.ajax({ url: "https://xxx", dataType: "json"}),
$.ajax({ url: "https://yyy", dataType: "json"}),
$.ajax({ url: "https://zzz", dataType: "json"}),
]).then(
(data) => {
// 只對(duì)第一個(gè)完成的操作進(jìn)行回調(diào)
console.log(data);
}, res => {
console.log("失敗");
})
async/await關(guān)鍵字
前面的Promise對(duì)象雖然讓回調(diào)代碼更加直觀坎拐,但在封裝時(shí)感覺(jué)還是挺不簡(jiǎn)潔的,而async
和await
可以讓異步的代碼更加直觀芹缔,能使我們編寫(xiě)異步代碼時(shí)更加貼近于同步代碼的寫(xiě)法坯癣。使用這兩個(gè)關(guān)鍵字可以控制異步操作是否暫停,async
關(guān)鍵字用來(lái)聲明操作中存在異步乖菱,只需在操作前聲明這個(gè)關(guān)鍵字即可(注意聲明了async
的函數(shù)的返回值將會(huì)自動(dòng)轉(zhuǎn)為promise
對(duì)象),而在async
關(guān)鍵字下的異步操作如果存在需要等待的操作蓬网,可以使用await
關(guān)鍵字聲明窒所,舉例:
async function aaa() {
console.log(0);
let x = await $.ajax({ url: "https://xxx", dataType: "json" }, function(data) { console.log(data); })
// x接收異步操作返回的數(shù)據(jù)
// 由于這里使用了await等待,所以在該步會(huì)等待異步操作執(zhí)行
// 如果異步操作失敗成功則執(zhí)行后續(xù)步驟帆锋,否則不會(huì)進(jìn)行后面的操作
console.log(1);
let y = await $.ajax({ url: "https://yyy", dataType: "json" }, function(data) { console.log(data); })
console.log(2);
let z = await $.ajax({ url: "https://zzz", dataType: "json" }, function(data) { console.log(data); })
console.log(3);
}
上面是添加了async
和await
關(guān)鍵字以后的代碼吵取,其和沒(méi)添加的代碼結(jié)果有什么區(qū)別呢?如果是沒(méi)添加這兩個(gè)關(guān)鍵字的的情況锯厢,那么結(jié)果異步操作不管是否執(zhí)行成功皮官,一般都是先依次輸出0
/1
/2
,然后再輸出三個(gè)異步請(qǐng)求的回調(diào)方法(成功或者失斒导)捺氢;而添加了關(guān)鍵字以后,就會(huì)先去等待第一個(gè)await
的地方的執(zhí)行剪撬,并且判斷執(zhí)行是否成功摄乒,如果成功則繼續(xù)往后執(zhí)行,否則就報(bào)錯(cuò)不繼續(xù)執(zhí)行,所以就會(huì)先輸出0
馍佑,然后等待一段時(shí)間斋否,當(dāng)?shù)谝粋€(gè)await
地方執(zhí)行成功則繼續(xù)往后輸出1
,否則報(bào)錯(cuò)后停止拭荤,以此類(lèi)推...
注:
從上面可以看出代碼相比使用Promise
對(duì)象要更加的簡(jiǎn)潔茵臭,而其返回的對(duì)象(上面變量x、y舅世、z)接收的內(nèi)容旦委,實(shí)際上還是Promise
對(duì)象,所以可以理解成這兩個(gè)關(guān)鍵字就是對(duì)Promise
對(duì)象的進(jìn)一步封裝歇终。
注2:
async
和await
關(guān)鍵字組合操作的原理就是將函數(shù)拆分成一個(gè)個(gè)小的存在依賴(lài)關(guān)系的函數(shù)執(zhí)行社证,比如上面的實(shí)際上就可以看成如下代碼:
function aaa() {
console.log(0);
let x = $.ajax({
url: "https://xxx",
dataType: "json"
}, function(data) {
console.log(data);
// 第一個(gè)await執(zhí)行成功則執(zhí)行后續(xù)內(nèi)容
console.log(1);
let y = $.ajax({
url: "https://yyy",
dataType: "json"
}, function(data) {
console.log(data);
// 第二個(gè)await執(zhí)行成功則執(zhí)行后續(xù)內(nèi)容
console.log(2);
let z = $.ajax({
url: "https://zzz",
dataType: "json"
}, function(data) {
console.log(data);
// 第三個(gè)await執(zhí)行成功則執(zhí)行后續(xù)內(nèi)容
console.log(3);
})
})
})
}
可以看出這個(gè)和最開(kāi)始回調(diào)地獄的代碼一樣,但是通過(guò)兩個(gè)關(guān)鍵字修飾了以后评凝,達(dá)到了簡(jiǎn)化代碼的目的
注3:
對(duì)于異步失敗的操作處理追葡,如ajax,此時(shí)如果請(qǐng)求失敗是不會(huì)有返回的奕短,比如下面代碼:
async function aaa() {
console.log(0);
let x = await $.ajax({ url: "https://xxx", dataType: "json" }, function(data) { console.log(data); })
// 這里如果請(qǐng)求失敗將會(huì)沒(méi)有返回值宜肉,此時(shí)x就會(huì)變成undefined,導(dǎo)致下面那句報(bào)錯(cuò)
console.log(x);
}
所以為了能夠?qū)κ〉恼?qǐng)求進(jìn)行處理翎碑,我們可以使用try{}catch(e){}
來(lái)捕捉處理谬返,舉例:
async function aaa() {
console.log(0);
try{
let x = await $.ajax({ url: "https://xxx", dataType: "json" }, function(data) { console.log(data); })
console.log(x);
} catch(e) {
// 捕捉失敗的請(qǐng)求
console.log(e);
}
}
或者封裝到Promise
對(duì)象里操作也可以,舉例:
function ajax() {
// 封裝成Promise對(duì)象
return new Promise((resolve, reject) =>
$.ajax({
url: "https://xxx",
dataType: "json",
success(data) {
// then里調(diào)用
resolve(data);
},
error(e) {
// 失敗時(shí)catch捕捉
reject(e);
}
})
);
}
async function aaa() {
console.log(0);
let x = await ajax().then(data => data).catch(e => false);
// 異步成功則只執(zhí)行then日杈,返回data遣铝,失敗則捕捉并返回false
console.log(x);
}