前言
常用JS的代碼片段
//連接數組
let arrA =[10,20,30]
let arrB = [40,50,60]
let arrC = [...arrA,...arrB]
//數組去掉重復項
let arrD = [30,30,40,40,50,50]
let arrE =[...new Set(arrD)]
//查找索引
[10,20,30].indexOf(20)
//遍歷數組
[10,20,30].forEach((value,index)=>{
console.log(`${value} ${index}`)
})
//映射新數組
let arrF = [10,20,30].map(v=>v*2)
//檢驗數組中每個元素
[10,20,30].every(v=>v>10)
//是否有元素通過測試
[10,20,30].some(v=>v>10)
//過濾數組
[10,20,30].filter(v=>v>10)
//獲取對象的key切威、value育特、es6遍歷數組
Object.keys({a:1,b:2},{a:3,b:11})
Object.values({a:1,b:2},{a:3,b:11})
Object.entries({a:1,b:2},{a:3,b:11})
//獲取對象里元素數量
Object.keys({a:1,b:2},{a:3,b:11}).length
前言
ECMAScript是ECMA組織制定的腳本語言規(guī)范,該規(guī)范來源于網景的Javascript和微軟的Jscript先朦,ECMAScript-262明確的定義了JavaScript缰冤。最初,JavaScript由Brendan Eich發(fā)明喳魏,最先出現在Navigator 2.0瀏覽器中锋谐。ES大會定期整理并發(fā)布新的JS版本。
ES版本 | 發(fā)布時間 | 新增特性 |
---|---|---|
ES5 | 200911 | 擴展了Object截酷、Array涮拗、Function等的功能 |
ES6(2015) | 201506 | 類、模塊化迂苛、剪頭函數三热、函數參數默認值、模版字符串三幻、解構賦值就漾、延展操作符、對象屬性簡寫(對象擴展念搬,還有計算屬性)抑堡、Promise、let朗徊、const(塊級作用域)首妖、新數據結構Set、Map爷恳、Symbol |
ES7(2016) | 201603 | Array.prototype.includes()有缆、指數操作符 |
ES8(2017) | 201706 | async/await、Object.values()、Object.entries()棚壁、String padding杯矩、函數參數列表結尾允許逗號、Object.getOwnPropertyDescriptors()等 |
延展操作符嚴格來說袖外,不算ES6標準史隆,但是babel支持,就寫在這里了
ES5我就不說了曼验,主要說說其他幾個都更新了什么逆害,另外可以使用babel將其他語法轉換為es5,此外babel還支持裝飾器蚣驼、async-await因此,就是瀏覽器有些不支持es6等更高級的語法相艇,也還是可以基于babel來使用更高級的語法
部分ES6 新增特性說明
新數據結構
Set(不可重復元素集合)颖杏、Map、Symbol
類
JS語言原來實現面向對象是直接通過原型的方式進行坛芽,這就讓Java留储、Object-c、C#等面向對象語言的程序員比較難以理解了咙轩。隨著JS能做的事情增多获讳,因此,才會原生的引入class這個概念活喊。我們來看看下邊這段代碼丐膝,來看看類與繼承的代碼如何寫,此次需要注意的是钾菊,繼承的時候帅矗,super要寫在子類構造函數的最頂部。另外煞烫,從源碼層面可以看出浑此,class、extends滞详、constructor都是prototype的語法糖
class Animal {
constructor(name, feature) {
this.name = name
this.feature = feature
}
toString() {
console.log('name:' + this.name + ',feature:' + this.feature)
// return 'Animal done'
}
}
var animal = new Animal('monky', 'beauty')//實例化
animal.toString()
console.log(animal.hasOwnProperty('name'))//true
console.log(animal.hasOwnProperty('toString'))//false
console.log(animal.__proto__.hasOwnProperty('toString'))//true
//繼承
class Cat extends Animal {
constructor(action) {
super('cat', 'sex')
this.action = action
}
toString(){
// console.log(super.toString())
super.toString()
}
}
var cat = new Cat('catch')
cat.toString()
console.log(cat instanceof Cat)//true
console.log(cat instanceof Animal)//true
模塊化(Module)
ES6實現了模塊化凛俱,于是告別了基于commonjs的標準,使用require.js的AMD模式和sea.js的CMD模式來實現模塊化的古老方式料饥。
模塊化主要基于export(導出蒲犬,還有export default)和import(引用,還有import * as)兩個關鍵字來實現岸啡,這就為JS引入了一個相對容易理解的封裝的概念(以前是基于函數作用域和閉包暖哨,通過底層直接實現,現在可以優(yōu)雅的通過關鍵字原生實現)
export定義了對外開放的接口,import定義了引用哪些接口篇裁。模塊化為js創(chuàng)建了比較容易理解的命名空間沛慢,防止函數的命名沖突。
export
export var name = 'Ryan'//導出變量
export const sort = Math.sqrt//導出常量
export {name, sort}//導出對象(模塊)
export function aModule(){return 1000}//導出函數
import
import defaultMethod,{otherMethod} from 'aModule'//xxx.js
箭頭(Arrow)函數
1.=>是function的縮寫
2.箭頭函數與包圍它的代碼共享同一個this达布。這種情況下团甲,就很好的解決了this的指向問題。我在寫一個錢包服務器的時候黍聂,做過這樣一個事情躺苦,通過定義var self = this,或者var that = this來引用外圍的this产还,也就是我想用其他function中的this匹厘,我就需要想將其復制到一個全局的或者是類的屬性上。借助=>脐区,就不需要這種模式了愈诚。箭頭函數的書寫形式如下
()=>100
v=>x+1
(a,b)=>a-b
()=>{var=u}//return undefined
p=>{return 100}
箭頭函數與bind的注意事項
箭頭函數和bind方法,每次被執(zhí)行后都會返回一個新的函數引用牛隅,因此炕柔,如果還需要對函數的引用做些事情的話(例如卸載監(jiān)聽器),那么你還需要保存之前的引用媒佣。以bind為例匕累,我們說明一下:
class PauseMenu extends React.Component {
componentWillMount() {
AppStateIOS.addEventListener('change', this.onAppPaused.bind(this))
}
componentWillUnmount() {
AppStateIOS.removeEventListener('change', this.onAppPaused.bind(this))
}
onAppPaused(event) { }
}
此處,因為bind每次都會調用一個新的函數引用默伍,因此欢嘿,造成卸載的不是原來的監(jiān)聽,造成卸載失敗也糊。此處應該修改為
class PauseMenu extends React.Component {
constructor(props) {
super(porps)
this._onAppPaused = this.onAppPaused.bind(this)
}
componentWillMount() {
AppStateIOS.addEventListener('change', this._onAppPaused)
}
componentWillUnmount() {
AppStateIOS.removeEventListener('change', this._onAppPaused)
}
onAppPaused(event) { }
}
那么基于剪頭函數际插,我們還可以使用箭頭函數來做,因為箭頭函數會共享包圍它的this显设,因此框弛,這樣就可以簡單明了的處理返回新的函數引用的問題了。
class PauseMenu extends React.Component {
componentWillMount() {
AppStateIOS.addEventListener('change', this.onAppPaused)
}
componentWillUnmount() {
AppStateIOS.removeEventListener('change', this.onAppPaused)
}
onAppPaused = event => { }
}
函數參數默認值
function foo(height = 50,color = '#fff")
如果不使用函數默認值捕捂,就會有個小問題瑟枫。
function foo (height,color)
{
var height = height||50
var color = color||'red'
}
如果參數的布爾值是false,例如指攒,foo(0慷妙,''),因為0的布爾值是false允悦,這樣height的取值將會是50膝擂,color也會取red,因此,函數的默認值不僅能使代碼簡潔架馋,也幫助規(guī)避一些風險
模版字符串
let name = 'name'+first+''+last+''
使用模版字符串狞山,就簡潔了很多。從此告別 +號拼接字符串的尷尬叉寂,并且還支持多行字符串
let name = `name is ${first} ${last}`
let names = `
xiaohong
zhangzhang
dada
`
解構賦值
解構賦值可以方便快速的從數組或者對象中提取賦值給定義的變量萍启。
獲取數組中的值
從數組中獲取值并賦值到變量中,變量的順序與數組中對象順序對應屏鳍。
var foo = [1,2,3,4,5]
var [one,twe,three] = foo
console.log(one)//1
console.log(twe)//2
console.log(three)//3
如果想要會略某些值勘纯,則可以
var [first,,last] = foo
console.log(first)//1
console.log(last)//5
也可以先聲明變量
var a,b
[a,b] = [1,2]
console.log(a)//1
console.log(b)//2
如果沒有從數組中獲取到值,可以為變量設置一個默認值
var a,b
[a=5,b=7]=[1]
console.log(a)//1
console.log(b)//7
方便的交互兩個變量的值
var a=1
var b = 3
[a,b]=[b,a]
console.log(a)//3
console.log(b)//1
獲取對象中的值
const student={
name:'xxx',
age:'19',
city:'bj'
}
const {name, age,city}=student
console.log(name)//xxx
console.log(age)//19
console.log(city)//bj
延展操作符(Spread operator)
延展操作符 = ...可以在函數調用/數組構造時钓瞭,將數組表達式或者string在語法層面展開驳遵,還可以在構造對象時,將對象表達式按key-value的方式展開山涡。
函數調用
function(...iterableObj)
數組構造或者字符串
[...iterableObj,'4',...'hello',6]
es2018下構造對象時堤结,進行克隆或者屬性拷貝
let objClone={...obj}
應用場景
function sum(x,y,x){
return x+y+z
}
const numbers = [1,2,3]
不使用延展操作符
sum.apply(null, numbers)
使用延展操作符
sum(...numbers)
或者在構造數組時
如果沒有展開語法,只能組合使用push佳鳖,splice,concat,slice
將已有數組元素變?yōu)樾聰到M的一部分
const people=['jan','tom']
const person = ['ali',...people,'alliance','ketty']
console.log(person)//Ali,jan,tom,alliance,ketty
另外媒惕,還有一個例子
var arr =[1,2,3]
var arr2=[...arr]
arr2.push(4)
console.log(arr2)//1,2,3,4
展開語法與Obj.assign()行為一致系吩,都是執(zhí)行淺拷貝,也就是只遍歷一層妒蔚,不會遍歷父對象相關的數據
var arr1=[0,1,2]
var arr2=[3,4,5]
var arr3=[...arr1,...arr2] 等同于var arr4 = arr1.concat(arr2)
es2018中增加了對對象的支持
var obj1 = {foo:1,foo2:2}
var obj2={foo3:12,foo4:30}
var clonedObj={...obj1}
var mergedObj={...obj1,...obj2}
...在react中的應用
我們封裝組件的時候穿挨,會對外公開props用于參數傳遞。我們一般都會使用...props進行參數的傳遞與遍歷數據
<CustomComponent name='Jine' age={21} />
等價于
const params = {
name:'jine',
age:21
}
<CustomComponent {...params} />
我們還可以配合結構賦值肴盏,避免傳入一些不必要的參數
var param = {
name:123,
title:456,
type:789
}
var {type,...other} = param
<CustomComponent type='normal' number={2} {...other} />
等價于
<CustomComponent type='normal' number={2} name=123,title=456/>
對象屬性簡寫
設置一個對象時科盛,不指定屬性名
es5
const name=ming,age=18,city=Shanghai
const student={
name:name,
age:age,
city:city
}
es6
const name=ming,age=18,city=Shanghai
const student={
name,
age,
city
}
Promise
Promise是異步編程的一種解決方案,對callback做進一步的封裝和優(yōu)化菜皂。Promise由社區(qū)提出和實現贞绵,es6將其寫進了語言標準,并統(tǒng)一了用法恍飘,提供了原生的promise支持榨崩。此處詳細解答,請看我的另外一篇blog
let章母、const
let母蛛、const提供塊級作用域,之前只能通過函數+閉包的方式來模擬塊級作用域乳怎。那么let與const的區(qū)別呢彩郊?let聲明可變變量,類型和值都可以改變醉者。const聲明不變變量(可以認為是常量瘫拣,聲明后就不能再次賦值了)暑脆,聲明變量后箍铭,必須馬上初始化颅崩,不能留到以后賦值赵誓。
es5
{var a=19}
console.log(a)//19
es6
{
let a=20
}
console.log(a)//a is not defined
部分ES7新增特性說明
Array.prototype.includes()
用來判斷一個數組是否包含指定的值菲宴,包含返回true艰管,不包含返回false
arr.includes(x)等價于arr.indexOf(x)>0
指數操作符(**)
** 與Math.pow(..)等效的計算結果
Math.pow(2,10)等價于2**10
部分ES8新增特性說明
async/await
異步轉同步前方,可以看我的另外一片blog狈醉。我們主要是用async/await來順序獲取異步函數返回值。
另外惠险,依托于Promise.all還可以實現await的并發(fā)調用苗傅,這樣就避免了同步調用的時間等待,提高了程序的整體效率班巩。
async function charCountAdd(data1, data2) {
const [d1, d2] = await Promise.all([charCount(data1), charCount(data2)])
// const d1 = await charCount(data1)
// const d2 = await charCount(data2)
return d1 + d2
}
charCountAdd('Hello','Hi').then(console.log)
function charCount(data){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(data.length)
},5000)
})
}
//捕捉錯誤渣慕,可以使用try catch,也可以在then后追加catch
Object.values()
Object.values()類似于Object.keys()抱慌,返回Object自身的所有屬性值逊桦,不包括繼承的值。
const obj={a:12,b:13,c:14}
es7
const vals=Object.keys(obj).map(key=>obj[key])
console.log(vals)//12,13,14
es8
const vals =Object.values(obj)
console.log(vals)//12,13,14
Object.entries
Object.entries()函數返回一個給定對象自身可枚舉屬性的鍵值對的數組
const obj={a:12,b:13,c:14}
es7
Object.keys(obj).forEach(key=>{
console.log('key:'+key+'values:'+obj[key])//12,13,14
})
es8
for (let [key,value] of Object.entries(obj)){
console.log('key: ${key} values: ${value}')//12,13,14
}
String padding
es8中抑进,新增String.prototype.padStart(targetLength,[padString])和String.prototype.padEnd允許將空字符串或者其他字符串添加到原始字符串的開頭或者結尾
console.log('0.0'.padStart(4,'10'))//10.0
console.log('0.0'.padStart(20))// 0.0
console.log('0.0'.padEnd(4,'0'))//0.00
console.log('0.0'.padEnd(10,'0'))//0.000000000
編碼規(guī)范
單引號强经、雙引號
node中,單引號雙引號有很多的時候是可以混用的寺渗,因此匿情,此處也是一個重點,如果不注意信殊,很容易出現奇怪的錯誤炬称。在JSON中,全部請使用雙引號涡拘,在其他地方也請使用雙引號玲躯,如果在雙引號中又存在引號,請使用單引號做區(qū)分鳄乏。
分號
必須記得加分號府蔗,不加分號就跟耍流氓一樣。
縮進汞窗、空格姓赤、大括號、小括號仲吏、逗號不铆、數組蝌焚、對象等的基本格式
通過IDE協助縮進,具體方式請根據不同的IDE進行總結誓斥,本處簡單介紹webstrom的使用方式----全選之后只洒,剪切代碼,然后重新粘貼到代碼編輯文本輸入框中劳坑。
變量聲明
如果是ES5的話永遠都要加var毕谴,如果是ES6.....如果是ES7....
命名
變量命名:名詞+小駝峰
方法命名:動詞開頭+名詞+小駝峰
類命名(ES6開始有類嘍,MD以前都是原型....艸):大駝峰
常量命名:大寫字母 + 下劃線
文件命名:小寫字母 + 下劃線
中間件(包)命名:小寫字母
==和===
==用于直接比較數值的情況
===用于其他情況
字面量
這個字面量的相關說明來源于樸靈的深入淺出距芬,我在這里做一下搬運工涝开。書中說“請盡量使用{}、[]替代new Object()框仔、new Array()舀武,不要使用string(new String)、bool(new Boolean)离斩、number(new Number)對象類型”银舱,當然,此處應該更多的是針對ES5下的相關語法跛梗,如果是ES6寻馏、ES7應該完全不一樣了。(需要補充ES6核偿、ES7的相關語法和規(guī)范)
作用域
依然會存在ES5诚欠、ES6、ES7不一致的地方宪祥。
ES5中沒有塊級語句聂薪,最多是通過with來增加一個臨時的塊級變量復用區(qū)域家乘,但是with又不是特別安全的蝗羊。
ES6有了塊級區(qū)域,變量范圍跟java差不多了
數組
數組不要用for in進行循環(huán)仁锯,詳見代碼:
var foo = [];
foo[100] = 100;
//循環(huán)1
for (var i in foo) {
console.log(i);
}
//循環(huán)2
for (var i = 0; i < foo.length; i++) {
console.log(i);
}
循環(huán)1只會執(zhí)行一次耀找,循環(huán)2則會執(zhí)行0~100,因此數組就不要for in 了 业崖,因為野芒,我們可能希望遍歷數組中的全部數據,而不是非undefine的數據双炕。
異步
異步回調第一個參數應該是錯誤指示狞悲。
類與模塊
依然會存在ES5、ES6妇斤、ES7不一致的地方摇锋。(之后做整理)
類繼承丹拯、導出都不一樣。
在es5下荸恕,類繼承推薦的方式是
function Socket(options){
//....
stream.Stream.call(this);
//....
}
util.inherits(Socket,stream.Stream);
最佳實踐的理論
1.注釋要寫成/** */的形式乖酬,方面工具導出
2.遵循原來項目的編碼規(guī)范
3.基于JSLint或者JSHint進行代碼質量管理,可以在編輯器中提醒不規(guī)范的信息融求,我們可以增加.jshintrc文件咬像。這個可以去github上去查找并學習。
4.代碼提交的hook腳本生宛,例如precommit县昂,這個需要學習整理。
5.持續(xù)集成茅糜,一方面七芭,持續(xù)集成代表著代碼質量的掃描,可以定時掃描或者觸發(fā)式掃描蔑赘,另一方面狸驳,可以通過集中的平臺統(tǒng)計代碼質量的好壞趨勢,根據統(tǒng)計結果可以判定團隊中的個人對編碼規(guī)范的執(zhí)行情況缩赛,決定用寬松的質量管理還是嚴格的質量管理耙箍。
6.可以使用CoffeeScript規(guī)范(編譯式的js,還有typescript等)酥馍,來避免一些不必要的編碼規(guī)范的問題
說明 | 規(guī)則或方法 |
---|---|
空格 | 利用IDE的排版功能自動增加需要的空格辩昆。(+、-旨袒、*汁针、/、%砚尽、(施无、)等操作符前后都增加空格) |
縮進 | 利用IDE的排版功能自動增加需要的縮進。與Java編碼規(guī)范相同 |
變量聲明 | 使用var聲明變量 |
單引號必孤、雙引號 | json中必須使用雙引號猾骡,es5 use strict下也必須使用雙引號,其他地方單引號雙引號可以隨便用 |
分號 | 必須增加分號在語句的結尾 |
變量命名 | 小駝峰 |
方法命名 | 小駝峰敷搪,動詞開頭 |
類命名 | 大駝峰 |
常量命名 | 都是大寫字母兴想,使用下劃線隔開每個單詞 |
文件命名 | 都是小寫字母,使用下劃線隔開每個單詞 |
對外文件命名 | 都是小寫字母赡勘,使用下劃線開頭嫂便,并使用下劃線隔開每個單詞 |
包名 | 都是小寫字母,仿照npm庫進行包的命名 |
比較操作 | 盡量使用===闸与,數值比較可以用== |