在進(jìn)一步學(xué)習(xí)react之前,你需要對(duì)js的新語(yǔ)法有一定的了解辑鲤,因?yàn)橛锌赡茉趓eact項(xiàng)目中我們會(huì)一遍又一遍的使用呻粹,如果你對(duì)js新語(yǔ)法有足夠的了解菩掏,你可以跳過(guò)這一節(jié)魂角。
變量
b變量是一種標(biāo)識(shí)符號(hào),你可以在接下來(lái)的程序中使用它智绸,變量在使用前必須要聲明。我們有三種方式來(lái)聲明變量:var访忿,let和const瞧栗,這三種方式在聲明之后的使用中會(huì)有所不同。
1. var
在ES2015之前海铆,var是聲明變量的唯一方式迹恐,它有下面這些特點(diǎn):
如果你給一個(gè)未聲明的變量賦值,結(jié)構(gòu)可能有所不同卧斟。在現(xiàn)代環(huán)境中殴边,如果嚴(yán)格模式開(kāi)啟憎茂,你會(huì)得到一個(gè)錯(cuò)誤,而在舊的環(huán)境中(或者嚴(yán)格模式被禁止)你將會(huì)得到一個(gè)全局變量锤岸。
如果你聲明了一個(gè)變量但是沒(méi)給它賦值竖幔,這個(gè)變量將是undefined,直到你給其賦值為止是偷。
var a //typeof a ==='undefined'
你可以重復(fù)聲明一個(gè)同一個(gè)變量拳氢,后面的值會(huì)覆蓋掉前面的值。
var a = 1
var a = 2
你也可以使用var同時(shí)聲明多個(gè)變量并賦值蛋铆。
var a =1, b=2
在函數(shù)外用var聲明的變量都是全局變量馋评,在任何地方都能訪問(wèn)到它,而在函數(shù)里面用var聲明變量刺啦,就只有局部作用域留特,這個(gè)變量只能在函數(shù)內(nèi)部訪問(wèn),其它地方都不能訪問(wèn)玛瘸。重要的一點(diǎn)是var聲明的變量沒(méi)有塊級(jí)作用域蜕青,只有函數(shù)作用域。
在函數(shù)內(nèi)部捧韵,不管var聲明的變量定義在函數(shù)的什么位置市咆,哪怕是最后,它仍然能夠在代碼開(kāi)始處獲取到再来,因?yàn)閖avascript執(zhí)行前會(huì)把所有的變量聲明移動(dòng)到最頂部(變量提升)蒙兰。為了避免這種結(jié)果,所以每次都在函數(shù)最開(kāi)始聲明變量芒篷。
2.使用let
let是ES6提出的新特性搜变,它比var多了一個(gè)快作用域,它的作用域限制在定義它的塊针炉,語(yǔ)句或表達(dá)式挠他。
現(xiàn)代js開(kāi)發(fā)者可能只選擇使用let,而完全丟棄var篡帕,在函數(shù)外使用let定義的變量不會(huì)變成一個(gè)全局變量殖侵,這點(diǎn)適合var聲明的變量完全不一致的。
3.使用const
使用let或者var聲明的變量在程序后面可能被更改镰烧,或者重新定義拢军,但是如果用const聲明的值,它將不能再被更改怔鳖。
const a = 'hello'
但是如果使用const聲明了一個(gè)常量茉唉,它的值是一個(gè)對(duì)象,那么還是可以去更改這個(gè)常量的屬性值的。
const a ={a:1}
a.a =2 //a.a的值會(huì)被改成2
const和let一樣度陆,也擁有塊級(jí)作用域艾凯,我們會(huì)用const去聲明一個(gè)在后面的代碼中不會(huì)改變的值。
箭頭函數(shù)
箭頭函數(shù)是ES6中最具影響力的改變懂傀,并且在今天得到廣泛的使用趾诗,它們和普通函數(shù)還是有一些區(qū)別的。從代碼書寫角度來(lái)看鸿竖,箭頭函數(shù)能夠讓我們以一種更簡(jiǎn)短的語(yǔ)法來(lái)定義一個(gè)函數(shù):
const oldFunction = function(){
//...
}
變成:
const arrowFunction = () => {
//...
}
如果這個(gè)函數(shù)體只有一行表達(dá)式沧竟,你可以省略花括號(hào)并且只寫成一行:
const myFunction = () => doSomething()
也可以通過(guò)括弧來(lái)傳遞參數(shù):
const myFunction = (param1,param2) => doSomething(param1,param2)
如果只有一個(gè)參數(shù),括弧也可以被省略掉
const myFunction = param => doSomething(param)
箭頭函數(shù)對(duì)于一些簡(jiǎn)單的函數(shù)定義非常有用缚忧。
1.箭頭函數(shù)的return
箭頭函數(shù)有一個(gè)隱藏的return悟泵,如果箭頭函數(shù)的函數(shù)體只是一個(gè)值,你可以不用return就能獲得一個(gè)返回值闪水。注意糕非,花括號(hào)需省略掉:
const myFunction = () => 'test'
myFunction() //test
如果返回值是一個(gè)對(duì)象,那么就要注意需要用括弧包括這個(gè)對(duì)象球榆,否則會(huì)認(rèn)為是箭頭函數(shù)的函數(shù)體朽肥,報(bào)語(yǔ)法錯(cuò)誤:
const myFunction = () => ({a:1})
myFunction() //{a:1}
2.箭頭函數(shù)的this
this在js中是一個(gè)復(fù)雜的概念,不同的函數(shù)上下文或者不同的javascript模式(嚴(yán)格或不嚴(yán)格)都會(huì)影響this的指向持钉。對(duì)于我們來(lái)說(shuō)衡招,掌握好this的概念非常重要,因?yàn)榧^函數(shù)在這點(diǎn)上和普通函數(shù)有著完全不一樣的區(qū)別每强。
在一個(gè)對(duì)象中始腾,對(duì)象有一個(gè)普通函數(shù)定義的方法,在這里this指向是這個(gè)對(duì)象本身:
const obj = {
name: 'Tom',
age: 16,
writeName: function() {
console.log(this.name)
}
}
obj.writeName() //Tom
調(diào)用obj.writeName()將會(huì)打印“Tom”
而箭頭函數(shù)的this繼承的是當(dāng)前執(zhí)行上下文空执,箭頭函數(shù)至始至終都沒(méi)有綁定this浪箭,所以this值將會(huì)在調(diào)用堆棧中查找,如果使用箭頭函數(shù)來(lái)定義上面的對(duì)象辨绊,結(jié)果將是打印“undefined”
const obj = {
name: 'Tom',
age: 16,
writeName: () => {
console.log(this.name)
}
}
obj.writeName() //undefined
因?yàn)檫@一點(diǎn)奶栖,箭頭函數(shù)不適合在對(duì)象方法中使用。
箭頭函數(shù)也不能作為構(gòu)造函數(shù)使用门坷,否則在創(chuàng)建一個(gè)對(duì)象時(shí)會(huì)拋出一個(gè)TypeError錯(cuò)誤宣鄙。
在DOM綁定事件的時(shí)候,如果使用箭頭函數(shù)作為事件的回調(diào)默蚌,里面的this指向?yàn)閣indow框冀,而如果是普通函數(shù)作為事件回調(diào),this指向的則是該DOM:
const ele = document.querySelector('#ele')
ele.addEventListener('click',() => {
// this === window
})
const ele = document.querySelector('#ele')
ele.addEventListener('click',function() {
// this === ele
})
使用擴(kuò)展運(yùn)算符(...)操作數(shù)組和對(duì)象
擴(kuò)展運(yùn)算符...在現(xiàn)代javascript中是一種非常有用的操作方式敏簿。我們可以從操作數(shù)組開(kāi)始熟悉這一操作:
cosnt a = [1, 2, 3]
然后你可以這樣創(chuàng)建一個(gè)新數(shù)組
cosnt b = [...a, 4, 5, 6] // [1, 2, 3, 4, 5, 6]
你也可以復(fù)制一個(gè)數(shù)組
cosnt c = [...a] // [1, 2, 3]
同樣的,你也可以這里來(lái)復(fù)制一個(gè)對(duì)象
const newObj = { ...oldObj }
如果是一個(gè)字符串使用...,我們會(huì)用字符串中的每個(gè)字符創(chuàng)建一個(gè)數(shù)組
const str = 'Hello'
const arr = [...str] // [H, e, l, l, o]
...運(yùn)算符還可以用來(lái)很方便的傳遞函數(shù)參數(shù)
cosnt func = (param1,param2) => {}
cosnt paramArr = [1, 2]
func(...paramArr)
//在ES6之前你可能使用f.apply(null,a)來(lái)傳遞參數(shù)惯裕,但是這樣不美觀温数,可讀性也很差
擴(kuò)展運(yùn)算符在數(shù)組解構(gòu)中運(yùn)用:
const arr = [1, 2, 3, 4, 5]
[a1, a2, ...a3] = arr
/**
*a1 = 1
*a2 = 2
*a3 = [3, 4, 5]
**/
擴(kuò)展運(yùn)算符在對(duì)象解構(gòu)中運(yùn)用:
const {p1, p2, ...p3} = {
p1: 1,
p2: 2,
p3: 3,
p4: 4
}
p1 // 1
p2 // 2
p3 // {p3: 3, p4: 4}
對(duì)象和數(shù)組的解構(gòu)賦值
第一個(gè)例子,我們使用解構(gòu)語(yǔ)法定義一些變量:
const person = {
firstName: 'Tom',
age: 18,
gender: 'boy'
}
const {firstName: name, age} = person
name // 'Tom'
age // 18
在這個(gè)例子中我們定義了兩個(gè)變量:name和age蜻势,name的值是person.firstName,age的值是person.age够傍,如果變量名和對(duì)象的屬性名一致的話,可以省略寫,也就是說(shuō):
const {firstName: name, age} = person
// 等同于const {firstName: name, age: age} = person
同樣的浴韭,這樣的寫法在數(shù)組中也起作用:
const arr = [1, 2, 3, 4, 5]
const [a1, a2] = arr
a1 // 1
a2 // 2
如果我們想創(chuàng)建第三個(gè)變量连霉,這個(gè)變量是數(shù)組arr中的第5個(gè)值:
const arr = [1, 2, 3, 4, 5]
const [a1, a2, , , a3 ] = arr
a1 // 1
a2 // 2
a3 // 5
模板字符串
在ES6中模板字符串是一種新的聲明字符串的方式翠语,非常有用酣难。第一眼看上去時(shí),它的語(yǔ)法很簡(jiǎn)單晚缩,只是在聲明字符串時(shí)使用反引號(hào)(`)替換單引號(hào)(‘’)或雙引號(hào)(“”):
const str1 = `my test string`
但是實(shí)際上他們很不一樣待笑,因?yàn)樗麄兲峁┝瞬簧俦扔谩颉啊苯⒌钠胀ㄗ址疀](méi)有的特性:
- 方便定義多行字符串
- 方便在字符串中使用變量或者表達(dá)式
多行字符串
ES6之前,定義多行字符串是比較麻煩的事:
const str =
'first line\n \
second line'
或者
const str = 'first line\n' + 'second line'
使用模板字符串會(huì)非常簡(jiǎn)單和美觀:
const str = `
first line
second line
`
并且定義時(shí)輸入的空白字符也會(huì)得到保留。
插值語(yǔ)法
我們可以使用${...}語(yǔ)法在模板字符串中插入變量或者表達(dá)式:
const name = 'Tom'
const str = `name is ${name}`
str // 'name is Tom'
const str1 = `age is ${10+8}`
str1 // `age is 18
const str2 = `gender is ${false?'male':'female'}`
str2 // gender is female
Classes(類)
JavaScript有一種非常罕見(jiàn)的實(shí)現(xiàn)繼承的方式:原型繼承浅悉,但與大多數(shù)其他流行的編程語(yǔ)言的繼承實(shí)現(xiàn)不同,后者是基于類的。來(lái)自其他語(yǔ)言的人很難理解原型繼承的復(fù)雜性飞醉,因此ECMAScript委員會(huì)決定在原型繼承之上撒上語(yǔ)法糖,這樣它就像基于類的繼承在其他流行實(shí)現(xiàn)中的工作方式。
這很重要:底層下的JavaScript仍然相同,您可以通常的方式訪問(wèn)對(duì)象原型。
1.一個(gè)類的定義
下面就是一個(gè)簡(jiǎn)單的類定義:
class People {
constructor(name) {
this.name = name
}
sayHello() {
return 'Hello, I am ' + this.name + '.'
}
}
let people_tom = new People('Tom')
people_tom.sayHello()
// "Hello, I am Tom."
當(dāng)一個(gè)對(duì)象初始化后按价,consturctor方法將會(huì)被調(diào)用,并且傳遞參數(shù)笙瑟,這個(gè)對(duì)象也可以調(diào)用類中聲明的方法楼镐。
2.類繼承
一個(gè)類可以從其它類進(jìn)行擴(kuò)展,通過(guò)這個(gè)擴(kuò)展類建立的對(duì)象往枷,將會(huì)繼承所有類的方法框产。如果繼承的類具有與層次結(jié)構(gòu)中較高的類之一具有相同名稱的方法,則最接近的方法優(yōu)先:
class Student extends People {
sayHello() {
return super.sayHello() + ' I am a student.'
}
}
const student_tom = new Student('Tom')
student_tom.sayHello()
// "Hello, I am Tom. I am a student."
類沒(méi)有顯式的類變量聲明错洁,但您必須初始化構(gòu)造函數(shù)中的所有變量秉宿。在類中,您可以引用調(diào)用super()的父類屯碴。
3.靜態(tài)方法
通常描睦,方法是在實(shí)例上定義的,而不是在類上定義的导而,現(xiàn)在靜態(tài)方法在類上執(zhí)行:
class People {
static genericHello() {
return 'Hello'
}
}
People.genericHello()
//Hello
4.取值函數(shù)(getter)和存值函數(shù)(setter)
您可以添加以get或set為前綴的方法來(lái)創(chuàng)建getter和setter忱叭,它們是根據(jù)您正在執(zhí)行的操作執(zhí)行的兩個(gè)不同的代碼:訪問(wèn)變量或修改其值。
對(duì)某個(gè)屬性設(shè)置存值函數(shù)和取值函數(shù)今艺,攔截該屬性的存取行為韵丑。
class People {
constructor(name) {
this._name = name
}
set name(newName) {
this._name = newName
}
get name() {
return this._name.toUpperCase();
}
}
let p1 = new People('Tom')
p1._name // Tom
p1.name // TOM
p1.name = 'Jack'
p1._name // 'Jack'
p1.name // 'JACK'
如果您只有一個(gè)getter,則無(wú)法設(shè)置該屬性虚缎,并且任何嘗試這樣做的操作都會(huì)被忽略:
class People {
constructor(name) {
this._name = name
}
get name() {
return this._name
}
}
let p1 = new People('Tom')
p1._name // Tom
p1.name // Tom
p1.name = 'Jack'
p1._name // 'Tom
p1.name // 'Tom'
如果您只有一個(gè)setter撵彻,則可以更改該值,但不能從外部訪問(wèn)它:
class People {
constructor(name) {
this._name = name
}
set name(newName) {
this._name = newName
}
}
let p1 = new People('Tom')
p1._name // Tom
p1.name //undefined
p1.name = 'Jack'
p1._name // 'Jack'
p1.name //undefined
Promises
Promise是在JavaScript中處理異步代碼的一種方法遥巴,避免了在代碼中寫太多回調(diào)的問(wèn)題千康。
所謂Promise,簡(jiǎn)單說(shuō)就是一個(gè)容器铲掐,里面保存著某個(gè)未來(lái)才會(huì)結(jié)束的事件(通常是一個(gè)異步操作)的結(jié)果拾弃。從語(yǔ)法上說(shuō),Promise 是一個(gè)對(duì)象摆霉,從它可以獲取異步操作的消息豪椿。Promise 提供統(tǒng)一的 API奔坟,各種異步操作都可以用同樣的方法進(jìn)行處理。
Async函數(shù)使用promises API作為基礎(chǔ)搭盾,因此理解它們是很基本的咳秉,即便在較新的代碼中,您可能會(huì)使用異步函數(shù)而不是promises鸯隅。
1.創(chuàng)建一個(gè)promise
我們使用new Promise()來(lái)初始化一個(gè)promise對(duì)象:
let isDone = true
const testPromise = new Promise((resolve, reject) => {
if (isDone) {
const result = 'success!'
resolve(result)
} else {
const result = 'failed!'
reject(result)
}
})
正如您所看到的澜建,promise會(huì)檢查已完成的全局常量,如果值為true蝌以,我們使用resolve傳回一個(gè)值炕舵,否則使用reject傳回一個(gè)值。在上面的例子中我們只返回一個(gè)字符串跟畅,但它也可以是一個(gè)對(duì)象咽筋。
2.使用promise
我們使用上面創(chuàng)建的promise對(duì)象作為示例:
testPromise.then(res =>{
console.log(res)
}).catch(err => {
console.log(err)
})
promise并使用then回調(diào)等待它解析,如果有錯(cuò)誤徊件,它將在catch回調(diào)中處理它奸攻。
3.鏈?zhǔn)綄懛?/h4>
一個(gè)promise對(duì)象可以返回另一個(gè)promise,因此可以使用鏈?zhǔn)降膶懛ǎ?/p>
const statusFunc = response => {
if (response.status >= 200 && response.status < 300) {
return Promise.resolve(response)
}
return Promise.reject(new Error(response.statusText))
}
const toJson = response => response.json()
fetch('/todos.json')
.then(statusFunc)
.then(toJson)
.then(data => {
console.log('Success虱痕!', data)
})
.catch(error => {
console.log('Failed', error)
})
在這個(gè)例子中睹耐,我們調(diào)用fetch()去讀取todos.json文件,然后創(chuàng)建一個(gè)promises鏈皆疹。
運(yùn)行fetch()會(huì)返回一個(gè)響應(yīng)疏橄,該響應(yīng)具有許多屬性:
- status,表示HTTP狀態(tài)代碼的數(shù)值
- statustext略就,狀態(tài)消息捎迫,如果請(qǐng)求成功,則為OK
statusFunc方法讀取JSON文件數(shù)據(jù)表牢,返回是一個(gè)promise窄绒;
toJson方法把上一步驟中json數(shù)據(jù)通過(guò)json()方法轉(zhuǎn)為json對(duì)象,它也是返回一個(gè)promise崔兴;
這一長(zhǎng)串的方法會(huì)發(fā)生什么樣的事情呢彰导?鏈中的第一個(gè)promise是我們定義的方法statusFunc,它檢查響應(yīng)狀態(tài)敲茄,如果實(shí)在200到300之間位谋,就是reslove狀態(tài),否則就是失敗的reject狀態(tài)堰燎,如果是reject狀態(tài)掏父,就會(huì)跳出后面所有的promise,直接被catch()所捕獲到秆剪,記錄失敗信息赊淑。
如果是成功的狀態(tài)爵政,下一個(gè)promise會(huì)把上一步promise的返回值當(dāng)作輸入值來(lái)做處理。
4.錯(cuò)誤處理
我們使用catch方法來(lái)處理promise中的錯(cuò)誤陶缺,當(dāng)promise鏈中的任何內(nèi)容失敗并引發(fā)錯(cuò)誤或reject時(shí)钾挟,代碼執(zhí)行都調(diào)轉(zhuǎn)到鏈中最近的catch()語(yǔ)句中,此時(shí)的catch方法的輸入會(huì)是代碼執(zhí)行的異潮グ叮或者是reject方法的輸入值掺出。
5.Promise.all()
如果你定義了一個(gè)promises列表,需要等待所有promise都有執(zhí)行結(jié)果后再進(jìn)行下一步處理伶贰,Promise.all()是一個(gè)方便的處理方法:
const p1 = fetch('/test1.json')
const p2 = fetch('/test2.json')
Promise.all([p1, p2])
.then(res => {
console.log('results: ', res)
})
.catch(err => {
console.error(err)
})
而ES6的解構(gòu)賦值語(yǔ)法也可以這樣來(lái)寫:
Promise.all([p1, p2]).then(([res1, res2]) => {
console.log('Results', res1, res2)
})
6.Promise.race()
Promise.race()會(huì)在您傳遞給它的一個(gè)promise有執(zhí)行結(jié)果后立即運(yùn)行蛛砰,并且只處理一次后面回調(diào),執(zhí)行的是第一個(gè)執(zhí)行完成的promise的結(jié)果:
const p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, 'first')
})
const p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'second')
})
Promise.race([p1,p2]).then(result => {
console.log(result) // 'second'
})
ES6 modules簡(jiǎn)介
ES Modules是用于處理模塊的ECMAScript標(biāo)準(zhǔn)黍衙。雖然Node.js多年來(lái)一直使用CommonJS標(biāo)準(zhǔn),但是在瀏覽器中從未有過(guò)模塊系統(tǒng)荠诬,直到ES6中modules標(biāo)準(zhǔn)的制定琅翻,瀏覽器才開(kāi)始實(shí)施這個(gè)標(biāo)準(zhǔn),現(xiàn)在ES模塊在現(xiàn)代瀏覽器Chrome柑贞,Safari方椎,Edge和Firefox中都得到了支持(從60版開(kāi)始)。
模塊功能非常有用钧嘶,您可以封裝各種功能棠众,并將此功能公開(kāi)給其他JS文件使用。
1.ES modules語(yǔ)法
導(dǎo)入一個(gè)模塊的語(yǔ)法很簡(jiǎn)單:
import package from 'module-name'
模塊是一個(gè)使用export關(guān)鍵字導(dǎo)出一個(gè)或多個(gè)值(對(duì)象有决,函數(shù)或變量)的js文件闸拿。 下面即為一個(gè)簡(jiǎn)單的模塊:
// test.js
export default str => str.toUpperCase()
在例子中,模塊定義了單個(gè)的default export书幕,因此它可以是匿名函數(shù)新荤,否則,它需要一個(gè)名稱來(lái)區(qū)別于其它導(dǎo)出台汇。然后任何其他js模塊都可以通過(guò)導(dǎo)入test.js來(lái)導(dǎo)入它提供的功能:
import toUpperCase from './uppercase.js'
之后我們就可以在js代碼中使用:
toUpperCase('test') //'TEST'
2.其它import/export選項(xiàng)
在前面的例子中苛骨,我們創(chuàng)建了一個(gè)默認(rèn)的導(dǎo)出,但是有時(shí)候我們可能需要在一個(gè)js文件中導(dǎo)出多個(gè)內(nèi)容:
const a = 1
const b = 2
const c = 3
export { a, b, c }
在其它js中的引用可以有多種寫法:
- 引入所有的export
import * from 'module'
- 使用結(jié)構(gòu)賦值引入一部分的export
import { a, b } from 'module'
- 方便起見(jiàn)苟呐,你可以使用as重命名任何export
import { a, b as test } from 'module'
- 您可以按名稱導(dǎo)入默認(rèn)export和任何非默認(rèn)export痒芝,這種方式在react中比較常見(jiàn)
import React, { Component } from 'react'
持續(xù)更新中
上一篇:React快速上手1-react安裝
下一篇:React快速上手3-JSX快速入門