大前端面試——JavaScript

什么是閉包碟婆?閉包是有權(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.原型和原型鏈

image.png

(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ù)分享。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末殖侵,一起剝皮案震驚了整個(gè)濱河市摔蓝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌愉耙,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拌滋,死亡現(xiàn)場(chǎng)離奇詭異朴沿,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)败砂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)赌渣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人昌犹,你說(shuō)我怎么就攤上這事坚芜。” “怎么了斜姥?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵鸿竖,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我铸敏,道長(zhǎng)缚忧,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任杈笔,我火速辦了婚禮闪水,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蒙具。我一直安慰自己球榆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布禁筏。 她就那樣靜靜地躺著持钉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪篱昔。 梳的紋絲不亂的頭發(fā)上右钾,一...
    開(kāi)封第一講書(shū)人閱讀 51,521評(píng)論 1 304
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼舀射。 笑死窘茁,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的脆烟。 我是一名探鬼主播山林,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼邢羔!你這毒婦竟也來(lái)了驼抹?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤拜鹤,失蹤者是張志新(化名)和其女友劉穎框冀,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體敏簿,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡明也,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了惯裕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片温数。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蜻势,靈堂內(nèi)的尸體忽然破棺而出撑刺,到底是詐尸還是另有隱情,我是刑警寧澤握玛,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布够傍,位于F島的核電站,受9級(jí)特大地震影響挠铲,放射性物質(zhì)發(fā)生泄漏王带。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一市殷、第九天 我趴在偏房一處隱蔽的房頂上張望愕撰。 院中可真熱鬧,春花似錦醋寝、人聲如沸搞挣。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)囱桨。三九已至,卻和暖如春嗅绰,著一層夾襖步出監(jiān)牢的瞬間舍肠,已是汗流浹背搀继。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留翠语,地道東北人叽躯。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像肌括,于是被迫代替她去往敵國(guó)和親点骑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355