什么是閉包碟婆?閉包是有權(quán)限訪(fǎng)問(wèn)其它函數(shù)作用域內(nèi)的變量的一個(gè)函數(shù)。
在js中惕稻,變量分為全局變量和局部變量竖共,局部變量的作用域?qū)儆诤瘮?shù)作用域,在函數(shù)執(zhí)行完以后作用域就會(huì)被銷(xiāo)毀俺祠,內(nèi)存也會(huì)被回收肘迎,但是由于閉包是建立在函數(shù)內(nèi)部的子函數(shù)甥温,由于其可訪(fǎng)問(wèn)上級(jí)作用域的原因,即使上級(jí)函數(shù)執(zhí)行完妓布,作用域也不會(huì)被銷(xiāo)毀姻蚓,此時(shí)的子函數(shù)——也就是閉包,便擁有了訪(fǎng)問(wèn)上級(jí)作用域中變量的權(quán)限匣沼,即使上級(jí)函數(shù)執(zhí)行完以后作用域內(nèi)的值也不會(huì)被銷(xiāo)毀狰挡。
閉包解決了什么?本質(zhì)上释涛,閉包就是將函數(shù)內(nèi)部和函數(shù)外部連接起來(lái)的一座橋梁加叁。由于閉包可以緩存上級(jí)作用域,這樣函數(shù)外部就可以訪(fǎng)問(wèn)到函數(shù)內(nèi)部的變量唇撬。
閉包的應(yīng)用場(chǎng)景ajax請(qǐng)求成功的回調(diào) 一個(gè)事件綁定的回調(diào)方法 setTimeout的延時(shí)回調(diào) 一個(gè)函數(shù)內(nèi)部返回另一個(gè)匿名函數(shù)
閉包的優(yōu)缺點(diǎn)優(yōu)點(diǎn):讓代碼更加規(guī)范它匕、簡(jiǎn)介 缺點(diǎn):使用閉包過(guò)多,內(nèi)存消耗大窖认,造成內(nèi)存的泄露
2.原型和原型鏈
(1).所有的引用類(lèi)型都有一個(gè)proto(隱式原型)屬性豫柬,屬性值是一個(gè)普通的對(duì)象 (2).所有的函數(shù)除了有proto屬性,還都有一個(gè)prototype(顯式原型)屬性扑浸,屬性值是一個(gè)普通的對(duì)象 (3).所有引用類(lèi)型的proto屬性指向它構(gòu)造函數(shù)的prototype
當(dāng)一個(gè)對(duì)象調(diào)用自身不存在的屬性/方法時(shí)烧给,會(huì)先去它的proto上查找,也就是它的構(gòu)造函數(shù)的prototype喝噪;如果沒(méi)有找到础嫡,就會(huì)去該構(gòu)造函數(shù)的prototype的proto指向的上一級(jí)函數(shù)的prototype中查找,再往上會(huì)指向?qū)ο蟮膒rototype酝惧,最后指向null榴鼎。這樣一層一層向上查找的關(guān)系會(huì)形成一個(gè)鏈?zhǔn)浇Y(jié)構(gòu),稱(chēng)為原型鏈晚唇。
3.ES5繼承和ES6繼承
ES5:組合式繼承檬贰,先創(chuàng)建子類(lèi)的實(shí)例對(duì)象,然后再將父類(lèi)的方法通過(guò)call方法添加到this上缺亮,通過(guò)原型和構(gòu)造函數(shù)的機(jī)制來(lái)實(shí)現(xiàn)
示例:
// 定義一個(gè)父類(lèi)
function Parent() {
this.name = '爸爸'
this.age = 50
this.sex = '男'
}
// 在父類(lèi)的原型上添加一個(gè)方法
Parent.prototype.play = function () {
console.log('去打麻將')
}
// 定義一個(gè)子類(lèi)
function Child(name) {
this.name = name
// 第一步:繼承父類(lèi)的屬性
Parent.call(this)
}
// 第二步:實(shí)現(xiàn)子類(lèi)繼承父類(lèi)的方法(兒子從爸爸那里學(xué)會(huì)了打麻將)
Child.prototype = new Parent()
// 第三步:找回丟失的構(gòu)造函數(shù)
Child.prototype.constructor = Child
復(fù)制代碼
ES6:先創(chuàng)建父類(lèi)的實(shí)例對(duì)象this(所以必須先調(diào)用父類(lèi)的super()方法,然后在用子類(lèi)的構(gòu)造函數(shù)修改this)桥言,通過(guò)class關(guān)鍵字定義類(lèi)萌踱,類(lèi)之間通過(guò)extends關(guān)鍵字實(shí)現(xiàn)繼承,子類(lèi)必須在constructor方法中調(diào)用super方法号阿。因?yàn)樽宇?lèi)沒(méi)有自己的this對(duì)象并鸵,而是繼承了父類(lèi)的this對(duì)象,然后對(duì)其加工扔涧,如果不調(diào)用super方法园担,子類(lèi)得不到this對(duì)象届谈。
示例:
// 定義一個(gè)父類(lèi)
class Parent {
constructor(name, sex) {
this.name = name
this.sex = sex
}
play() {
console.log(this.name + '打麻將超級(jí)垃圾')
}
speak() {
console.log('性別是:' + this.sex)
}
}
// 父類(lèi)的實(shí)例化
let father = new Parent('老高','男')
father.play() // 打麻將超級(jí)垃圾
father.speak() // 性別是男
// 創(chuàng)建一個(gè)子類(lèi)去繼承父類(lèi)
class Child extends Parent{
constructor(name, sex) {
// 調(diào)用父類(lèi)的 constructor
super(name, sex)
}
}
// 子類(lèi)的實(shí)例化
let son = new Child('小高', '女')
son.play()
son.speak()
復(fù)制代碼
4.原生AJAX請(qǐng)求步驟
五步使用法: (1).創(chuàng)建XMLHTTPRequest對(duì)象 (2).使用open方法設(shè)置和服務(wù)器的交互信息 (3).設(shè)置發(fā)送的數(shù)據(jù),開(kāi)始和服務(wù)器端交互 (4).注冊(cè)事件 (5).更新界面
Get請(qǐng)求:
// 第一步:創(chuàng)建異步對(duì)象
let xhr = new XMLHttpRequest()
// 第二步:設(shè)置請(qǐng)求的url參數(shù)弯汰,參數(shù)1是請(qǐng)求的類(lèi)型艰山,參數(shù)2是請(qǐng)求的url,可以攜帶參數(shù)
xhr.open('get', '/baidu.com?username=1')
// 第三步:設(shè)置發(fā)送的數(shù)據(jù)咏闪,開(kāi)始和服務(wù)端交互
xhr.send()
// 第四步:注冊(cè)事件onreadystatechange曙搬,當(dāng)狀態(tài)改變時(shí)會(huì)調(diào)用
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
// 第五步:如果到達(dá)這一步,說(shuō)明數(shù)據(jù)返回葱峡,請(qǐng)求的頁(yè)面是存在的
console.log(xhr.responseText)
}
}
復(fù)制代碼
POST請(qǐng)求:
// 第一步:創(chuàng)建異步對(duì)象
let xhr = new XMLHttpRequest()
// post請(qǐng)求一定要添加請(qǐng)求頭赶袄,不然會(huì)報(bào)錯(cuò)
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')
// 第二步:設(shè)置請(qǐng)求的url參數(shù)相满,參數(shù)1是請(qǐng)求的類(lèi)型,參數(shù)2是請(qǐng)求的url橡娄,可以攜帶參數(shù)
xhr.open('post', '/baidu.com')
// 第三步:設(shè)置發(fā)送的數(shù)據(jù),開(kāi)始和服務(wù)端交互
xhr.send('username=1&password=123')
// 第四步:注冊(cè)事件onreadystatechange癣籽,當(dāng)狀態(tài)改變時(shí)會(huì)調(diào)用
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
// 第五步:如果到達(dá)這一步挽唉,說(shuō)明數(shù)據(jù)返回,請(qǐng)求的頁(yè)面是存在的
console.log(xhr.responseText)
}
}
復(fù)制代碼
5.關(guān)于事件委托
什么是事件委托事件委托也叫事件代理才避,就是利用事件冒泡橱夭,只指定一個(gè)事件處理程序,就可以管理某一類(lèi)型的所有事件桑逝。
事件委托的作用(1).提高性能:每一個(gè)函數(shù)都會(huì)占用內(nèi)存空間棘劣,只需添加一個(gè)時(shí)間處理程序代理所有事件,所占用的內(nèi)存空間更少楞遏; (2).動(dòng)態(tài)監(jiān)聽(tīng):使用事件委托可以自動(dòng)綁定動(dòng)態(tài)添加的元素茬暇,即新增的節(jié)點(diǎn)不需要主動(dòng)添加也可以具有和其它元素一樣的事件。
實(shí)現(xiàn)方式我們先來(lái)看看寡喝,如果不用事件委托糙俗,需要綁定多個(gè)相同事件的時(shí)候是如何實(shí)現(xiàn)的:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
<li class="item">按鈕</li>
<li class="item">按鈕</li>
<li class="item">按鈕</li>
<li class="item">按鈕</li>
</ul>
</body>
<script>
window.onload = function () {
let lis = document.getElementsByClassName('item')
for (let i = 0; i < lis.length; i++) {
lis[i].onclick = function () {
console.log('用力的點(diǎn)我')
}
}
}
</script>
</html>
復(fù)制代碼
不使用事件委托,那就要遍歷每一個(gè)li元素预鬓,給每個(gè)li元素綁定一個(gè)點(diǎn)擊事件巧骚,這樣的做法非常耗費(fèi)內(nèi)存,如果有100個(gè)格二、1000個(gè)li元素劈彪,那對(duì)性能的影響是非常大的。
那么使用事件委托是怎么實(shí)現(xiàn)的呢顶猜?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul id="wrap">
<li class="item">按鈕</li>
<li class="item">按鈕</li>
<li class="item">按鈕</li>
<li class="item">按鈕</li>
</ul>
</body>
<script>
window.onload = function () {
let ul = document.getElementById('wrap')
ul.onclick = function (ev) {
// 獲取到事件對(duì)象
let e = ev || window.event
// 如果點(diǎn)擊的元素的calssName為item
if (e.target.className === 'item') {
console.log('用力的點(diǎn)我')
}
}
}
</script>
</html>
復(fù)制代碼
這樣一來(lái)沧奴,通過(guò)事件委托,只需要在li元素的父元素ul上綁定一個(gè)點(diǎn)擊事件长窄,通過(guò)事件冒泡的機(jī)制滔吠,就可以實(shí)現(xiàn)li的點(diǎn)擊效果纲菌。并且通過(guò)js動(dòng)態(tài)添加li元素,也能綁定點(diǎn)擊事件疮绷。
6.null不是一個(gè)對(duì)象翰舌,但為什么typeof null === object
原理是這樣的,不同的對(duì)象在底層都會(huì)表示為二進(jìn)制矗愧,在js中如果二進(jìn)制的前三位都為0灶芝,就會(huì)被判斷為object類(lèi)型,null的二進(jìn)制全為0唉韭,自然前三位也是0夜涕,所以typeof null === objcet。
7.關(guān)于深拷貝和淺拷貝
淺拷貝:只復(fù)制指向某個(gè)對(duì)象的指針属愤,而不復(fù)制對(duì)象本身女器,新舊對(duì)象還是共享同一塊內(nèi)存
實(shí)現(xiàn):
方法1:直接用=賦值
let obj1 = {a: 1}
let obj2 = obj1
復(fù)制代碼
方法2:Object.assign
let obj1 = {a: 1}
let obj2 = {}
Object.assign(obj2, obj1)
復(fù)制代碼
方法3:for in循環(huán)只遍歷第一層
let result = {}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] = obj[key]
}
}
return result
}
let obj1 = {
a: 1,
b: {
c: 2
}
}
let obj2 = shallowObj(obj1)
obj1.b.c = 3
console.log(obj2.b.c) // 3
復(fù)制代碼
深拷貝:方法1:用 JSON.stringify 把對(duì)象轉(zhuǎn)換成字符串,再用 JSON.parse 把字符串轉(zhuǎn)換成新的對(duì)象
let obj1 = {
a: 1,
b: 2,
}
let obj2 = JSON.parse(JSON.stringify(obj1))
復(fù)制代碼
方法2:采用遞歸去拷貝所有層級(jí)屬性
function deepClone(obj) {
// 如果傳入的值不是一個(gè)對(duì)象住诸,就不執(zhí)行
if (Object.prototype.toString.call(obj) !== '[object Object]') return
// 根據(jù)傳入值的類(lèi)型初始化返回結(jié)果
let result = obj instanceof Array ? [] : {}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 如果obj是個(gè)對(duì)象驾胆,就遞歸調(diào)用deepClone去遍歷obj的每一層屬性,如果不是對(duì)象就直接返回值
result[key] = Object.prototype.toString.call(obj[key]) === '[object Object]' ? deepClone(obj[key]) : obj[key]
}
}
return result
}
// 改進(jìn)判斷對(duì)象的方法
console.log(typeof null === 'object') // true
console.log(Object.prototype.toString.call(null) === '[object Object]') // false
復(fù)制代碼
方法3:lodash函數(shù)庫(kù)實(shí)現(xiàn)深拷貝
let obj1 = {
a: 1,
b: 2,
}
let obj2 = _.cloneDeep(obj1)
復(fù)制代碼
方法4:通過(guò)jQuery的extend方法實(shí)現(xiàn)深拷貝
let array = [1,2,3,4]
let newArray = $.extend(true,[],array) // true為深拷貝贱呐,false為淺拷貝
復(fù)制代碼
方法5:用slice實(shí)現(xiàn)對(duì)數(shù)組的深拷貝
let arr1 = ["1","2","3"]
let arr2 = arr1.slice(0)
arr2[1] = "9"
console.log(arr2) // ['1', '9', '3']
console.log(arr1) // ['1', '2', '3']
復(fù)制代碼
方法6:使用擴(kuò)展運(yùn)算符實(shí)現(xiàn)深拷貝
let obj1 = {brand: "BMW", price: "380000", length: "5米"}
let obj2 = { ...car, price: "500000" }
復(fù)制代碼
8.談?wù)刯s的垃圾回收機(jī)制
js擁有自動(dòng)的垃圾回收機(jī)制丧诺,當(dāng)一個(gè)值在內(nèi)存中失去引用時(shí),垃圾回收機(jī)制會(huì)根據(jù)特殊的算法找到它奄薇,并將其回收驳阎,釋放內(nèi)存。
標(biāo)記清除法(常用)(1).標(biāo)記階段:垃圾回收器會(huì)從根對(duì)象開(kāi)始遍歷馁蒂。每一個(gè)可以從根對(duì)象訪(fǎng)問(wèn)到的對(duì)象都會(huì)被添加一個(gè)標(biāo)識(shí)呵晚,于是這個(gè)對(duì)象就被標(biāo)識(shí)為可到達(dá)對(duì)象; (2).清除階段:垃圾回收器會(huì)對(duì)堆內(nèi)存從頭到尾進(jìn)行線(xiàn)性遍歷沫屡,如果發(fā)現(xiàn)有對(duì)象沒(méi)有被標(biāo)識(shí)為可到達(dá)對(duì)象饵隙,那么就將此對(duì)象占用的內(nèi)存回收,并且將原來(lái)標(biāo)記為可到達(dá)對(duì)象的標(biāo)識(shí)清除沮脖,以便進(jìn)行下一次垃圾回收操作金矛;
優(yōu)點(diǎn):實(shí)現(xiàn)簡(jiǎn)單 缺點(diǎn):可能會(huì)造成大量的內(nèi)存碎片
引用計(jì)數(shù)清除法(1).引用計(jì)數(shù)的含義就是跟蹤記錄每個(gè)值被引用的次數(shù),當(dāng)聲明了一個(gè)變量并將一個(gè)引用類(lèi)型賦值給該變量時(shí)勺届,這個(gè)值的引用次數(shù)就是1驶俊。相反,如果包含對(duì)這個(gè)值引用的變量又取得了另外一個(gè)值涮因,這個(gè)值的引用次數(shù)就減1。 (2).當(dāng)這個(gè)引用次數(shù)變成0時(shí)伺绽,則說(shuō)明沒(méi)有辦法再訪(fǎng)問(wèn)這個(gè)值了养泡,就可以將其所占的內(nèi)存空間給回收嗜湃。這樣,垃圾收集器下次再運(yùn)行時(shí)澜掩,就會(huì)釋放那些引用次數(shù)為0的值所占的內(nèi)存购披。
優(yōu)點(diǎn): (1).可即刻回收垃圾
缺點(diǎn): (1).計(jì)數(shù)器值的增減處理繁重 (2).實(shí)現(xiàn)繁瑣復(fù)雜 (3).循環(huán)引用無(wú)法回收
9.如何阻止事件冒泡和默認(rèn)事件
標(biāo)準(zhǔn)的DOM對(duì)象中可以使用事件對(duì)象的stopPropagation()方法來(lái)阻止事件冒泡,但在IE8以下中的事件對(duì)象通過(guò)設(shè)置事件對(duì)象的cancelBubble屬性為true來(lái)阻止冒泡
默認(rèn)事件通過(guò)事件對(duì)象的preventDefault()方法來(lái)阻止肩榕,而IE通過(guò)設(shè)置事件對(duì)象的returnValue屬性為false來(lái)阻止默認(rèn)事件
10.函數(shù)去抖和函數(shù)節(jié)流
函數(shù)去抖(debounce):當(dāng)調(diào)用函數(shù)n秒后刚陡,才會(huì)執(zhí)行該動(dòng)作,若在這n秒內(nèi)又調(diào)用該函數(shù)則取消前一次并重新計(jì)算執(zhí)行時(shí)間(頻繁觸發(fā)的情況下株汉,只有足夠的空閑時(shí)間筐乳,才執(zhí)行代碼一次)
function debounce(delay, cb) {
let timer
return function () {
if (timer) clearTimeout(timer)
timer = setTimeout(function () {
cb()
}, delay)
}
}
復(fù)制代碼
函數(shù)節(jié)流(throttle):函數(shù)節(jié)流的基本思想是函數(shù)預(yù)先設(shè)定一個(gè)執(zhí)行周期,當(dāng)調(diào)用動(dòng)作的時(shí)刻大于等于執(zhí)行周期則執(zhí)行該動(dòng)作乔妈,然后進(jìn)入下一個(gè)新周期(一定時(shí)間內(nèi)js方法只跑一次蝙云。比如人的眨眼睛,就是一定時(shí)間內(nèi)眨一次)
function throttle(cb, delay) {
let startTime = Date.now()
return function () {
let currTime = Date.now()
if (currTime - startTime > delay) {
cb()
startTime = currTime
}
}
}
復(fù)制代碼
11.談?wù)刯s的事件循環(huán)機(jī)制
程序開(kāi)始執(zhí)行之后路召,主程序則開(kāi)始執(zhí)行同步任務(wù)勃刨,碰到異步任務(wù)就把它放到任務(wù)隊(duì)列之中,等到同步任務(wù)全部執(zhí)行完后股淡,js引擎便去查看任務(wù)隊(duì)列有沒(méi)有可以執(zhí)行的異步任務(wù)身隐,將異步任務(wù)轉(zhuǎn)成同步任務(wù)并開(kāi)始執(zhí)行,執(zhí)行完同步任務(wù)后繼續(xù)查看任務(wù)隊(duì)列唯灵。這個(gè)過(guò)程是一直循環(huán)的贾铝,因此這個(gè)過(guò)程就是所謂的事件循環(huán),其中任務(wù)隊(duì)列也被稱(chēng)為事件隊(duì)列早敬。通過(guò)一個(gè)任務(wù)隊(duì)列忌傻,單線(xiàn)程的js實(shí)現(xiàn)了異步任務(wù)的執(zhí)行,給人的感覺(jué)好像是多線(xiàn)程的搞监。
12.箭頭函數(shù)和普通函數(shù)的區(qū)別
普通函數(shù): (1).this總是代表它的直接調(diào)用者 (2).在默認(rèn)情況下水孩,沒(méi)找到直接調(diào)用者,this指向window (3).在嚴(yán)格模式下琐驴,沒(méi)有直接調(diào)用者的函數(shù)中的this是undefined (4).使用call俘种,apply,bind綁定绝淡,this指的是綁定的對(duì)象
箭頭函數(shù): (1).在使用 => 定義函數(shù)的時(shí)候宙刘,this的指向是定義時(shí)所在的對(duì)象,而不是使用時(shí)所在的對(duì)象牢酵,bind()悬包、call()、apply()均無(wú)法改變指向 (2).不能用做構(gòu)造函數(shù)馍乙,也就是說(shuō)不能使用new命令布近,否則就會(huì)拋出一個(gè)錯(cuò)誤 (3).不能使用arguments對(duì)象垫释,但是可以使用…rest參數(shù) (4).不能使用yield命令 (5).沒(méi)有原型屬性
13.call()、apply()撑瞧、bind()的區(qū)別
call()棵譬、apply()、bind()是用來(lái)改變this的指向的
call():Function.call(obj, param1,param2,param3) 接收到的是param1预伺,param2订咸,param3三個(gè)參數(shù)
apply():Function.apply(obj, [param1,param2,param3]) 接收到的是param1,param2酬诀,param3三個(gè)參數(shù)
call和apply的區(qū)別是參數(shù)一個(gè)不用[]脏嚷,一個(gè)要用[]
bind():const newFn = Funtion.bind(obj, param1,param2) 返回值是一個(gè)函數(shù),需要()來(lái)調(diào)用 newFn(param3,param4) 接收到的是param1料滥,param2然眼,param3,param4四個(gè)參數(shù)
14.實(shí)現(xiàn)一個(gè)sleep函數(shù)
js不像java一樣有sleep()方法葵腹,但由于js是單線(xiàn)程的高每,可以利用偽死循環(huán)阻塞主線(xiàn)程來(lái)達(dá)到延遲執(zhí)行的效果
function sleep(delay) {
// 獲取一個(gè)初始時(shí)間
let startTime = new Date().getTime()
// 如果時(shí)間差小于延遲時(shí)間,就一直循環(huán)
while (new Date().getTime() - startTime < delay) {
continue
}
}
復(fù)制代碼
15.進(jìn)程和線(xiàn)程的區(qū)別
并發(fā)執(zhí)行的程序在執(zhí)行過(guò)程中分配和管理資源的基本單位践宴,是一個(gè)動(dòng)態(tài)概念鲸匿。
線(xiàn)程(thread):是cpu調(diào)度的最小單位(是建立在進(jìn)程基礎(chǔ)上的一次程序運(yùn)行單位),是進(jìn)程內(nèi)可調(diào)度的實(shí)體阻肩,比進(jìn)程更小的獨(dú)立運(yùn)行的基本單位带欢。
一個(gè)進(jìn)程有一個(gè)或多個(gè)線(xiàn)程,線(xiàn)程之間共同完成進(jìn)程分配下來(lái)的任務(wù)烤惊,打個(gè)比方: ● 假如進(jìn)程是一個(gè)工廠(chǎng)乔煞,工廠(chǎng)有它的獨(dú)立的資源 ● 工廠(chǎng)之間相互獨(dú)立 ● 線(xiàn)程是工廠(chǎng)中的工人,多個(gè)工人協(xié)作完成任務(wù) ● 工廠(chǎng)內(nèi)有一個(gè)或多個(gè)工人 ● 工人之間共享空間
再完善完善概念: ● 工廠(chǎng)的資源 -> 系統(tǒng)分配的內(nèi)存(獨(dú)立的一塊內(nèi)存) ● 工廠(chǎng)之間的相互獨(dú)立 -> 進(jìn)程之間相互獨(dú)立 ● 多個(gè)工人協(xié)作完成任務(wù) -> 多個(gè)線(xiàn)程在進(jìn)程中協(xié)作完成任務(wù) ● 工廠(chǎng)內(nèi)有一個(gè)或多個(gè)工人 -> 一個(gè)進(jìn)程由一個(gè)或多個(gè)線(xiàn)程組成 ● 工人之間共享空間 -> 同一進(jìn)程下的各個(gè)線(xiàn)程之間共享程序的內(nèi)存空間(包括代碼段柒室、數(shù)據(jù)集渡贾、堆等)
16.ES6、ES7雄右、ES8的新特性
ES6的特性
(1). 類(lèi)(class)對(duì)熟悉Java空骚、C、C++等語(yǔ)言的開(kāi)發(fā)者來(lái)說(shuō)擂仍,class一點(diǎn)都不陌生囤屹。ES6引入了class(類(lèi)),讓JS的面向?qū)ο缶幊套兊酶雍?jiǎn)單和易于理解逢渔。
(2).模塊化(Module)ES5不支持原生的模塊化肋坚,在ES6中模塊作為重要的組成部分被添加進(jìn)來(lái)。模塊的功能主要由export和import組成。每一個(gè)模塊都有自己?jiǎn)为?dú)的作用域智厌,模塊之間的相互調(diào)用關(guān)系是通過(guò) export 來(lái)規(guī)定模塊對(duì)外暴露的接口粟判,通過(guò)import來(lái)引用其它模塊提供的接口。同時(shí)還為模塊創(chuàng)造了命名空間峦剔,防止函數(shù)的命名沖突。
導(dǎo)出(export) ES6運(yùn)行在一個(gè)模塊中使用export來(lái)導(dǎo)出多個(gè)變量或函數(shù)
// 導(dǎo)出變量
export let name = 'gg'
// 導(dǎo)出常量
export const name = 'gg'
// 導(dǎo)出多個(gè)變量
let a = 2
let b = 4
export {a, b}
// 導(dǎo)出函數(shù)
export function myModule(someArg) {
return someArg
}
復(fù)制代碼
導(dǎo)入(import) 定義好模塊的輸出以后就可以在另外一個(gè)模塊通過(guò)import引用角钩。
import {myModule} from 'myModule'
import {a,b} from 'test'
復(fù)制代碼
(3).箭頭(Arrow)函數(shù)這是ES6中最令人激動(dòng)的特性之一吝沫。=>不只是關(guān)鍵字function的簡(jiǎn)寫(xiě),它還帶來(lái)了其它好處递礼。箭頭函數(shù)與包圍它的代碼共享同一個(gè)this,能很好的解決this的指向問(wèn)題惨险。
(4).函數(shù)參數(shù)默認(rèn)值ES6支持在定義函數(shù)的時(shí)候?yàn)槠湓O(shè)置默認(rèn)值,當(dāng)函數(shù)的參數(shù)為布爾值false時(shí)脊髓,可以規(guī)避一些問(wèn)題
// 使用默認(rèn)值
function foo(height = 50, color = 'red') {
//
}
// 不使用默認(rèn)值
function foo(height, color) {
let height = height || 50
let color = color || 'red'
}
復(fù)制代碼
(5).模板字符串
// 不使用模板字符串
let name = 'Your name is ' + first + ' ' + last + '.'
// 使用模板字符串
let name = `Your name is ${first} ${last}.`
復(fù)制代碼
(6).解構(gòu)賦值通過(guò)解構(gòu)賦值可以方便的交換兩個(gè)變量的值:
let a = 1
let b = 3
[a, b] = [b, a];
console.log(a) // 3
console.log(b) // 1
復(fù)制代碼
獲取對(duì)象中的值:
const student = {
name:'Ming',
age:'18',
city:'Shanghai'
}
const {name,age,city} = student
console.log(name) // "Ming"
console.log(age) // "18"
console.log(city) // "Shanghai"
復(fù)制代碼
(7).延展操作符(Spread operator)和剩余運(yùn)算符(rest operator)當(dāng)三個(gè)點(diǎn)(...)在等號(hào)右邊辫愉,或者放在實(shí)參上,是 spread運(yùn)算符
myFunction(...arr)
let arr1 = [1, 2, 3, 4]
let arr2 = [...arr1, 4, 5, 6]
console.log(arr2) // [1, 2, 3, 4, 4, 5, 6]
復(fù)制代碼
當(dāng)三個(gè)點(diǎn)(...)在等號(hào)左邊将硝,或者放在形參上恭朗,是 rest 運(yùn)算符
function myFunction(...arr) {
}
let [a,...temp]=[1, 2, 4]
console.log(a) // 1
console.log(temp) // [2, 4]
復(fù)制代碼
(8).對(duì)象屬性簡(jiǎn)寫(xiě)在ES6中允許我們?cè)谠O(shè)置一個(gè)對(duì)象的屬性的時(shí)候不指定屬性名
不使用ES6:
const name='Ming',age='18',city='Shanghai';
const student = {
name:name,
age:age,
city:city
};
console.log(student)//{name: "Ming", age: "18", city: "Shanghai"}
復(fù)制代碼
使用ES6:
const name='Ming',age='18',city='Shanghai'
const student = {
name,
age,
city
};
console.log(student)//{name: "Ming", age: "18", city: "Shanghai"}
復(fù)制代碼
(9).PromisePromise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案callback更加的優(yōu)雅依疼。它最早由社區(qū)提出和實(shí)現(xiàn)的痰腮,ES6 將其寫(xiě)進(jìn)了語(yǔ)言標(biāo)準(zhǔn),統(tǒng)一了用法律罢,原生提供了Promise對(duì)象膀值。
不使用ES6
setTimeout(function()
{
console.log('Hello') // 1秒后輸出"Hello"
setTimeout(function()
{
console.log('Hi') // 2秒后輸出"Hi"
}, 1000)
}, 1000)
復(fù)制代碼
使用ES6
let waitSecond = new Promise(function(resolve, reject)
{
setTimeout(resolve, 1000)
});
waitSecond
.then(function()
{
console.log("Hello") // 1秒后輸出"Hello"
return waitSecond
})
.then(function()
{
console.log("Hi") // 2秒后輸出"Hi"
})
復(fù)制代碼
(10).支持let與const在之前JS是沒(méi)有塊級(jí)作用域的,const與let填補(bǔ)了這方便的空白误辑,const與let都是塊級(jí)作用域沧踏。
let和var的區(qū)別: ● let沒(méi)有變量提升,存在暫時(shí)性死區(qū)巾钉,必須等let聲明完以后翘狱,變量才能使用 ● let變量不能重復(fù)聲明 ● let聲明的變量只在let代碼塊有效
ES7的特性
(1).Array.prototype.includes()includes() 函數(shù)用來(lái)判斷一個(gè)數(shù)組是否包含一個(gè)指定的值,如果包含則返回 true睛琳,否則返回false
let arr = ['react', 'angular', 'vue']
if (arr.includes('react')) {
console.log('react存在')
}
復(fù)制代碼
(2).指數(shù)操作符在ES7中引入了指數(shù)運(yùn)算符盒蟆,具有與Math.pow(..)等效的計(jì)算結(jié)果。
console.log(Math.pow(2, 10)) // 輸出1024
console.log(2**10) // 輸出1024
復(fù)制代碼
ES8的特性
(1).async/await在ES8中加入了對(duì)async/await的支持师骗,也就我們所說(shuō)的異步函數(shù)历等,這是一個(gè)很實(shí)用的功能。 async/await相當(dāng)于一個(gè)語(yǔ)法糖辟癌,解決了回調(diào)地獄的問(wèn)題
(2).Object.values()Object.values()是一個(gè)與Object.keys()類(lèi)似的新函數(shù)寒屯,但返回的是Object自身屬性的所有值,不包括繼承的值。
a: 1,
b: 2,
c: 3,
}
// 不使用Object.values()
const vals = Object.keys(obj).map(e => obj[e])
console.log(vals) // [ 1, 2, 3 ]
// 使用Object.values()
console.log(Object.values(obj)) // [ 1, 2, 3 ]
復(fù)制代碼
(3).Object.entriesObject.entries()函數(shù)返回一個(gè)給定對(duì)象自身可枚舉屬性的鍵值對(duì)的數(shù)組寡夹。
const obj = {
a: 1,
b: 2,
c: 3,
}
// 不使用Object.entries()
Object.keys(obj).forEach(key=>{
console.log('key:'+key+' value:'+obj[key])
})
//key:a value:1
//key:b value:2
//key:c value:3
// 使用Object.entries()
for(let [key,value] of Object.entries(obj1)){
console.log(`key: ${key} value:${value}`)
}
//key:a value:1
//key:b value:2
//key:c value:3
復(fù)制代碼
(4).String padding在ES8中String新增了兩個(gè)實(shí)例函數(shù)String.prototype.padStart和String.prototype.padEnd处面,允許將空字符串或其他字符串添加到原始字符串的開(kāi)頭或結(jié)尾
String.padStart(targetLength,[padString])
復(fù)制代碼
targetLength:當(dāng)前字符串需要填充到的目標(biāo)長(zhǎng)度。如果這個(gè)數(shù)值小于當(dāng)前字符串的長(zhǎng)度菩掏,則返回當(dāng)前字符串本身魂角。 padString:(可選)填充字符串。如果字符串太長(zhǎng)智绸,使填充后的字符串長(zhǎng)度超過(guò)了目標(biāo)長(zhǎng)度野揪,則只保留最左側(cè)的部分,其他部分會(huì)被截?cái)嗲评酰藚?shù)的缺省值為 " "斯稳。
console.log('0.0'.padStart(4,'10')) //10.0
console.log('0.0'.padStart(20))// 0.00
復(fù)制代碼
String.padEnd(targetLength,padString])
復(fù)制代碼
targetLength:當(dāng)前字符串需要填充到的目標(biāo)長(zhǎng)度。如果這個(gè)數(shù)值小于當(dāng)前字符串的長(zhǎng)度迹恐,則返回當(dāng)前字符串本身挣惰。 padString:(可選) 填充字符串。如果字符串太長(zhǎng)殴边,使填充后的字符串長(zhǎng)度超過(guò)了目標(biāo)長(zhǎng)度憎茂,則只保留最左側(cè)的部分,其他部分會(huì)被截?cái)啻赴叮藚?shù)的缺省值為 " "唇辨;
console.log('0.0'.padEnd(4,'0')) //0.00
console.log('0.0'.padEnd(10,'0'))//0.00000000
復(fù)制代碼
(5).函數(shù)參數(shù)列表結(jié)尾允許逗號(hào)
// 不使用ES8
//程序員A
let f = function(a,
b
) {
...
}
//程序員B
let f = function(a,
b, //變更行
c //變更行
) {
...
}
//程序員C
let f = function(a,
b,
c, //變更行
d //變更行
) {
...
}
// 使用ES8
//程序員A
let f = function(a,
b,
) {
...
}
//程序員B
let f = function(a,
b,
c, //變更行
) {
...
}
//程序員C
let f = function(a,
b,
c,
d, //變更行
) {
...
}
復(fù)制代碼
(6).Object.getOwnPropertyDescriptors()Object.getOwnPropertyDescriptors()函數(shù)用來(lái)獲取一個(gè)對(duì)象的所有自身屬性的描述符,如果沒(méi)有任何自身屬性,則返回空對(duì)象能耻。
const obj2 = {
name: 'Jine',
get age() { return '18' }
};
Object.getOwnPropertyDescriptors(obj2)
// {
// age: {
// configurable: true,
// enumerable: true,
// get: function age(){}, //the getter function
// set: undefined
// },
// name: {
// configurable: true,
// enumerable: true,
// value:"Jine",
// writable:true
// }
// }
復(fù)制代碼
17.性能監(jiān)控平臺(tái)是如何捕獲錯(cuò)誤的
全局捕獲通過(guò)全局的接口赏枚,將捕獲代碼集中寫(xiě)在一個(gè)地方,可以利用的接口有: (1).window.addEventListener(‘error’) / window.addEventListener(“unhandledrejection”) / document.addEventListener(‘click’) 等 (2).框架級(jí)別的全局監(jiān)聽(tīng)晓猛,例如aixos中使用interceptor進(jìn)行攔截饿幅,vue、react都有自己的錯(cuò)誤采集接口 (3).通過(guò)對(duì)全局函數(shù)進(jìn)行封裝包裹戒职,實(shí)現(xiàn)在在調(diào)用該函數(shù)時(shí)自動(dòng)捕獲異常 (4).對(duì)實(shí)例方法重寫(xiě)(Patch)栗恩,在原有功能基礎(chǔ)上包裹一層,例如對(duì)console.error進(jìn)行重寫(xiě)洪燥,在使用方法不變的情況下也可以異常捕獲
單點(diǎn)捕獲在業(yè)務(wù)代碼中對(duì)單個(gè)代碼塊進(jìn)行包裹磕秤,或在邏輯流程中打點(diǎn),實(shí)現(xiàn)有針對(duì)性的異常捕獲: (1).try…catch中throw err (2).專(zhuān)門(mén)寫(xiě)一個(gè)函數(shù)來(lái)收集異常信息捧韵,在異常發(fā)生時(shí)市咆,調(diào)用該函數(shù) (3).專(zhuān)門(mén)寫(xiě)一個(gè)函數(shù)來(lái)包裹其他函數(shù),得到一個(gè)新函數(shù)再来,該新函數(shù)運(yùn)行結(jié)果和原函數(shù)一模一樣蒙兰,只是在發(fā)生異常時(shí)可以捕獲異常
18.函數(shù)柯里化
在數(shù)學(xué)和計(jì)算機(jī)科學(xué)中磷瘤,柯里化是一種將使用多個(gè)參數(shù)的一個(gè)函數(shù)轉(zhuǎn)換成一系列使用一個(gè)參數(shù)的函數(shù)的技術(shù)
示例:
function add(a, b) {
return a + b;
}
// 執(zhí)行 add 函數(shù),一次傳入兩個(gè)參數(shù)即可
add(1, 2) // 3
// 假設(shè)有一個(gè) curry 函數(shù)可以做到柯里化
var addCurry = curry(add);
addCurry(1)(2) // 3
addCurry(1)(2) = add(1, 2)
復(fù)制代碼
實(shí)現(xiàn): (1).最簡(jiǎn)單的方式搜变,使用lodash庫(kù)的_.curry
function sum(a, b) {
return a + b
}
// 使用來(lái)自 lodash 庫(kù)的 _.curry
let curriedSum = _.curry(sum)
alert( curriedSum(1, 2) ) // 3
alert( curriedSum(1)(2) ) // 3
復(fù)制代碼
(2).自定義函數(shù)實(shí)現(xiàn)
function curry(func) {
return function curried(...args) {
if (args.length >= func.length) {
return func.apply(this, args)
} else {
return function(...args2) {
return curried.apply(this, args.concat(args2))
}
}
}
}
let currfn = curry(add)
console.log(currfn(1)(3)) // 4
復(fù)制代碼
19.new關(guān)鍵字做了什么采缚?
使用new操作符調(diào)用構(gòu)造函數(shù)實(shí)際上會(huì)經(jīng)歷以下4個(gè)步驟: (1).創(chuàng)建一個(gè)新對(duì)象 (2).將構(gòu)造函數(shù)的作用域賦給新對(duì)象(因此this就指向了這個(gè)新對(duì)象) (3).執(zhí)行構(gòu)造函數(shù)中的代碼(為這個(gè)新對(duì)象添加屬性、方法) (4).返回新對(duì)象
var obj = {}
obj.__proto__ = Base.prototype
Base.call(obj)
復(fù)制代碼
有想了解更多的小伙伴可以加Q群點(diǎn)擊鏈接加入群聊【web前端技術(shù)交流學(xué)習(xí)群】:https://jq.qq.com/?_wv=1027&k=pVe5r7RJ里面看一下挠他,應(yīng)該對(duì)你們能夠有所幫助扳抽。里面有許多學(xué)習(xí)資料和面試文檔 以及免費(fèi)的進(jìn)階技術(shù)分享。