前端面试总结--JS
基本數(shù)據(jù)類型
String、Number、Boolean、null、undefined
類型判斷
判斷基本數(shù)據(jù)類型用typeof: MDN
- typeof 'aaa' // string
- typeof 123 // number
- typeof true // boolean
- typeof null // object 因?yàn)閚ull和object的類型標(biāo)簽都是0,null是一個(gè)空對象指針
- typeof undefined // undefined
- typeof 函數(shù) // function
- typeof 其他對象 // object
判斷引用類型用instanceof: MDN
- instanceof 運(yùn)算符用來檢測 constructor.prototype 是否存在于參數(shù) object 的原型鏈上。
- 用instanceof來判斷基本數(shù)據(jù)類型會(huì)報(bào)錯(cuò)
如何判斷一個(gè)數(shù)據(jù)是NaN
NaN==NaN; // false isNaN(NaN); // true Number.isNaN(NaN); // true Object.is(NaN,NaN); // truenull與undefined區(qū)別
null 表示值被定義但是個(gè)空值,null是一個(gè)空對象指針;
undefined 表示變量聲明但未賦值;
參考:理解 | 堆內(nèi)存棧內(nèi)存釋放、null和{}、undefined的區(qū)別
作用域鏈
定義:訪問一個(gè)變量時(shí),會(huì)先在當(dāng)前作用域查找該變量,若找到就直接使用,若未找到就繼續(xù)向上一層查找,直到全局作用域。這種鏈?zhǔn)讲樵冴P(guān)系就是作用域鏈。
其實(shí)作用域鏈在函數(shù)定義時(shí)已經(jīng)確定了,作用域鏈?zhǔn)呛秃瘮?shù)定義時(shí)的位置相關(guān)的。在函數(shù)創(chuàng)建的時(shí)候創(chuàng)建一個(gè)包含外部對象(包括全局對象和所有包含自己的對象)的作用域鏈,儲(chǔ)存在內(nèi)部[[scope]]屬性中。函數(shù)執(zhí)行的時(shí)候會(huì)創(chuàng)建一個(gè)執(zhí)行環(huán)境,通過復(fù)制[[scope]]屬性中的對象,構(gòu)建執(zhí)行環(huán)境的作用域鏈,并把自己的活動(dòng)對象推向當(dāng)前作用域鏈的前端以此形成完整的作用域鏈。[[scope]]屬性中保存的是對可訪問變量對象的引用,而不是值的復(fù)制。
參考:函數(shù)的作用域鏈在定義時(shí)已經(jīng)確定!!
閉包
定義:閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。
優(yōu)點(diǎn):局部變量可被重用且不會(huì)被污染(變量私有化)。
缺點(diǎn):由于變量不會(huì)被回收,所以濫用閉包會(huì)導(dǎo)致內(nèi)存溢出,所以要及時(shí)釋放不再需要的閉包。
代碼:
閉包是在定義時(shí)確定的
參考:JS閉包的理解
事件處理機(jī)制
DOM事件流存在三個(gè)階段:事件捕獲階段→處于目標(biāo)階段→事件冒泡階段。
在捕獲階段觸發(fā)事件:addEventListener(event, listener, true)
在冒泡階段出發(fā)事件:addEventListener(event, listener, false), attachEvent(event,listener)
事件委托
<ul><li></li><li></li><li></li> </ul>window.onload = function(){ var UL = document.getElementById('ul');//委托ul上的點(diǎn)擊事件,將當(dāng)前點(diǎn)擊的li節(jié)點(diǎn)變?yōu)榧t色UL.onclick = function(ev){ var e = ev || window.event;var target = e.target || window.event.srcElement; //判斷target是否符合要求的元素節(jié)點(diǎn) if(target.tagName.toLowerCase() == 'li'){//將當(dāng)前點(diǎn)擊這個(gè)li節(jié)點(diǎn)變成紅色 target.style.backgroundColor = 'red';} }}如果我們需要在每一個(gè)li上綁定一個(gè)事件,就可以利用事件冒泡原理,將這些事件綁定到ul上,讓ul來代為處理。
優(yōu)點(diǎn):
- 提高性能。每個(gè)函數(shù)都會(huì)占用內(nèi)存,使用一個(gè)事件可減少內(nèi)存占用。
- 動(dòng)態(tài)監(jiān)聽。新增的節(jié)點(diǎn)無需再重新綁定事件也擁有和其他節(jié)點(diǎn)一樣的事件。
阻止事件冒泡
W3C的方法: event.stopPropagation()
IE的方法: event.cancelBubble =true
取消默認(rèn)事件
W3C的方法: event.preventDefault()
IE的方法: event.returnValue =false
垃圾回收機(jī)制
標(biāo)記清除:垃圾收集器在運(yùn)行的時(shí)候會(huì)給存儲(chǔ)在內(nèi)存中的所有變量都加上標(biāo)記(當(dāng)然,可以使用任何標(biāo)記方式)。然后,它會(huì)去掉環(huán)境中的變量以及被環(huán)境中的變量引用的變量的標(biāo)記。而在此之后再被加上標(biāo)記的變量將被視為準(zhǔn)備刪除的變量,原因是環(huán)境中的變量已經(jīng)無法訪問到這些變量了。最后,垃圾收集器完成內(nèi)存清除工作,銷毀那些帶標(biāo)記的值并回收它們所占用的內(nèi)存空間。
引用計(jì)數(shù):當(dāng)聲明了一個(gè)變量并將一個(gè)引用類型值賦值該變量時(shí),則這個(gè)值的引用次數(shù)就是1.如果同一個(gè)值又被賦給另外一個(gè)變量,則該值得引用次數(shù)加1。相反,如果包含對這個(gè)值引用的變量又取 得了另外一個(gè)值,則這個(gè)值的引用次數(shù)減 1。當(dāng)這個(gè)值的引用次數(shù)變成 0時(shí),則說明沒有辦法再訪問這個(gè)值了,因而就可以將其占用的內(nèi)存空間回收回來。這樣,當(dāng)垃圾收集器下次再運(yùn)行時(shí),它就會(huì)釋放那 些引用次數(shù)為零的值所占用的內(nèi)存。
參考:js垃圾回收機(jī)制和引起內(nèi)存泄漏的操作;JS垃圾回收機(jī)制;談?wù)凧S 垃圾回收機(jī)制
this的指向
方法調(diào)用模式:this指向?qū)ο蟆?br /> 函數(shù)調(diào)用模式:this指向window。
構(gòu)造器調(diào)用模式:this指向?qū)嵗?br /> call和apply調(diào)用:this指向傳入的第一個(gè)參數(shù)。
箭頭函數(shù):this指向定義時(shí)所在的對象,call和apply失效;不可以當(dāng)做構(gòu)造函數(shù);不可以使用arguments對象;不可以使用yield命令。
參考:call、apply和bind方法的用法以及區(qū)別;函數(shù)的四種調(diào)用模式及this指向;JS this指向總結(jié)
call、apply和bind的用法以及區(qū)別
它們作用都是改變函數(shù)運(yùn)行時(shí)this的指向。
function func (a,b,c) {}// call的第一個(gè)參數(shù)是要綁定給this的值,從第二個(gè)參數(shù)開始是接收的參數(shù)列表。 // 當(dāng)?shù)谝粋€(gè)參數(shù)為null、undefined的時(shí)候,this默認(rèn)指向window。 func.call(obj, 1, 2, 3)// apply接受兩個(gè)參數(shù),第一個(gè)參數(shù)是要綁定給this的值,第二個(gè)參數(shù)是一個(gè)參數(shù)數(shù)組。 // 當(dāng)?shù)谝粋€(gè)參數(shù)為null、undefined的時(shí)候,this默認(rèn)指向window。 func.apply(obj, [1,2,3])// bind和call很相似,第一個(gè)參數(shù)是this的指向,從第二個(gè)參數(shù)開始是接收的參數(shù)列表。 // 區(qū)別在于bind的返回值是一個(gè)改變了this指向的函數(shù),不會(huì)立即執(zhí)行,原來的函數(shù)this的指向是不變的。 func.bind(obj, 1, 2, 3)參考:call、apply和bind方法的用法以及區(qū)別;讓你弄懂 call、apply、bind的應(yīng)用和區(qū)別;「干貨」細(xì)說 call、apply 以及 bind 的區(qū)別和用法
原型和原型鏈
function SuperType(){this.property = true; } SuperType.prototype.getSuperValue = function(){return this.property; } var superInstance = new SuperType(); function SubType(){this.subProperty = false; } SubType.prototype.getSubValue = function(){return this.subProperty; } var subInstance = new SubType(); SubType.prototype = new SuperType(); SubType.prototype.getSubValue1 = function(){return this.subProperty; } var instance = new SubType(); alert(instance.getSuperValue()); //true //查找順序:instance→SubType.prototype→SuperType.prototype注意:subInstance.constructor = SuperType,因?yàn)镾ubType.prototype指向了另一個(gè)對象,導(dǎo)致constructor 被重寫了;同理,subInstance.getSubValue()也已經(jīng)訪問不到了。
如何實(shí)現(xiàn)繼承
1-經(jīng)典繼承(借用構(gòu)造函數(shù))
優(yōu)點(diǎn):可以在子類構(gòu)造函數(shù)中向父類構(gòu)造函數(shù)傳參;避免了引用類型的屬性被所有實(shí)例共享。
缺點(diǎn):方法都在構(gòu)造函數(shù)中定義,每次創(chuàng)建實(shí)例都會(huì)創(chuàng)建一遍方法,無法復(fù)用。
2-原型鏈繼承(借用原型鏈)
優(yōu)點(diǎn):方法可以復(fù)用。
缺點(diǎn):引用類型的屬性被所有實(shí)例共享;創(chuàng)建子類的實(shí)例時(shí),不能向父類傳參。
3-偽經(jīng)典繼承(組合繼承)
優(yōu)點(diǎn):融合原型鏈繼承和構(gòu)造函數(shù)繼承的優(yōu)點(diǎn),是 JavaScript 中最常用的繼承模式。
參考:js各種繼承方式和優(yōu)缺點(diǎn)的介紹
新建對象的方法
1-直接新建
let person = {sex: 'femail',age: '19',eat: function(){console.log('eating');} }2-工廠模式
function createAPerson(){let person = new Object();person.sex = 'femail';person.age = '19';person.eat = function(){console.log('eating');}return person; } let person = createAPerson();3-構(gòu)造函數(shù)模式
function Person(){this.sex = 'femail';this.age = '19';this.eat = function(){console.log('eating');} } let person = new Person();4-原型模式
function Person(){ } Person.prototype.sex = 'femail'; Person.prototype.age = '19'; Person.prototype.eat = function(){console.log('eating'); } let person = new Person();5-混合模式(組合使用構(gòu)造函數(shù)模式和原型模式)
function Person(){this.sex = 'femail';this.age = '19'; } Person.prototype.eat = function(){console.log('eating'); } let person = new Person();參考:js中對象與對象創(chuàng)建方法的各種方法
EventLoop
宏任務(wù)
宏任務(wù)的例子有很多,包括創(chuàng)建主文檔對象、解析HTML、執(zhí)行主線JavaScript代碼,更改當(dāng)前的URL、以及各種事件、setTimeout、setInterval等。
微任務(wù)
微任務(wù)是更小的任務(wù),主要包括Promise的回調(diào)函數(shù)、DOM發(fā)生變化。微任務(wù)需要盡可能快的、通過異步的方式執(zhí)行。
事件循環(huán)基于兩個(gè)基本原則
一次處理一個(gè)任務(wù)。
一個(gè)任務(wù)開始后直到運(yùn)行完成,不會(huì)被其他任務(wù)中斷。
參考:Promise自我修養(yǎng)之事件循環(huán)
怎么區(qū)分宏任務(wù)和微任務(wù)
宏任務(wù)是由宿主發(fā)起的,而微任務(wù)由JavaScript自身發(fā)起
參考鏈接:宏任務(wù)和微任務(wù)到底是什么?
MutationObserver
描述:監(jiān)視一個(gè)節(jié)點(diǎn)及其全部子節(jié)點(diǎn)樹的添加、移除元素,以及任何屬性變化的事件
應(yīng)用:群組組件監(jiān)聽disabled屬性
變量和函數(shù)的優(yōu)先級
函數(shù)提升優(yōu)先級高于變量提升,且不會(huì)被同名變量聲明覆蓋,但是會(huì)被變量賦值后覆蓋。而且存在同名函數(shù)與同名變量時(shí),優(yōu)先執(zhí)行函數(shù)。
參考:JS中變量提升與函數(shù)提升及其優(yōu)先級
堆內(nèi)存和棧內(nèi)存
參考:JavaScript棧內(nèi)存和堆內(nèi)存
深拷貝和淺拷貝
深拷貝和淺拷貝是針對引用數(shù)據(jù)類型的,比如數(shù)組和對象。基本數(shù)據(jù)類型不存在深淺拷貝之分。
淺拷貝:只復(fù)制引用,原對象屬性值改變,新的屬性值也會(huì)改變。(只復(fù)值第一層)
深拷貝:創(chuàng)建一個(gè)新的內(nèi)存,復(fù)制真正的值,原對象屬性值改變,新的屬性值不會(huì)受影響。(復(fù)制每一層)
淺拷貝數(shù)組:
// 方法1 let arr1 = [1,2,3,4,5]; let arr2 = [...arr1];// 方法2 let arr1 = [1,2,3,4,5]; let arr2 = arr1.slice(0)// 方法3 let arr1 = [1,2,3,4,5]; let arr2 = arr1.concat()淺拷貝對象:
// 方法1 var o2 = Object.assign({}, o1)// 方法2 var o2 = {...o1}深拷貝數(shù)組和對象:
// 方法1,需要求目標(biāo)對象(非 undefined,function) const obj2 = JSON.parse(JSON.stringify(obj1));// 方法2 function deepClone(item){const target = item.constructor === Array ? [] : {}; // 判斷復(fù)制的目標(biāo)是數(shù)組還是對象for(let keys in item){ // 遍歷目標(biāo)if(item.hasOwnProperty(keys)){if(item[keys] && typeof item[keys] === 'object'){ // 如果值是對象,就遞歸一下target[keys] = item[keys].constructor === Array ? [] : {};target[keys] = deepClone(item[keys]);}else{ // 如果不是,就直接賦值target[keys] = item[keys];}}}return target; }淺拷貝內(nèi)存分析
深拷貝內(nèi)存分析
參考:內(nèi)存分析-深淺拷貝
函數(shù)柯里化(Currying)
定義:柯里化是指通過函數(shù)調(diào)用繼續(xù)返回函數(shù)的方式,實(shí)現(xiàn)多次接收參數(shù)最后統(tǒng)一處理的函數(shù)編碼形式
優(yōu)點(diǎn):函數(shù)復(fù)用、延遲執(zhí)行、提前確認(rèn)
參考:詳解JS函數(shù)柯里化;「前端面試題系列6」理解函數(shù)的柯里化
數(shù)組的常用方法
arr.push() // 從后面添加元素,返回值為添加完后的數(shù)組的長度 arr.pop() // 從后面刪除元素,只能是一個(gè),返回值是刪除的元素 arr.shift() // 從前面刪除元素,只能刪除一個(gè) 返回值是刪除的元素 arr.unshift() // 從前面添加元素, 返回值是添加完后的數(shù)組的長度 arr.splice(i,n) // 刪除從i(索引值)開始之后的n個(gè)元素。返回值是刪除的元素 arr.concat() // 連接兩個(gè)數(shù)組 返回值為連接后的新數(shù)組 str.split() // 將字符串轉(zhuǎn)化為數(shù)組 arr.sort() // 將數(shù)組進(jìn)行排序,返回值是排好的數(shù)組,默認(rèn)是按照最左邊的數(shù)字進(jìn)行排序,不是按照數(shù)字大小排序的 arr.reverse() // 將數(shù)組反轉(zhuǎn),返回值是反轉(zhuǎn)后的數(shù)組 arr.slice(start,end) // 切去索引值start到索引值end的數(shù)組,不包含end索引的值,返回值是切出來的數(shù)組 arr.forEach(callback) // 遍歷數(shù)組,無return 即使有return,也不會(huì)返回任何值,并且會(huì)影響原來的數(shù)組 arr.map(callback) // 映射數(shù)組(遍歷數(shù)組),有return 返回一個(gè)新數(shù)組 arr.filter(callback) // 過濾數(shù)組,返回一個(gè)滿足要求的數(shù)組 arr.reduce(callback) // 對數(shù)組中的每個(gè)元素執(zhí)行一個(gè)由您提供的reducer函數(shù)(升序執(zhí)行),將其結(jié)果匯總為單個(gè)返回普通函數(shù)和構(gòu)造函數(shù)的區(qū)別
- 構(gòu)造函數(shù)也是一個(gè)普通函數(shù),創(chuàng)建方式和普通函數(shù)一樣,但是構(gòu)造函數(shù)習(xí)慣上首字母大寫;
- 調(diào)用方式不一樣,普通函數(shù)直接調(diào)用,構(gòu)造函數(shù)要用關(guān)鍵字new來調(diào)用;
- 調(diào)用時(shí),構(gòu)造函數(shù)內(nèi)部會(huì)創(chuàng)建一個(gè)新對象,就是實(shí)例,普通函數(shù)不會(huì)創(chuàng)建新對象;
- 構(gòu)造函數(shù)內(nèi)部的this指向?qū)嵗?#xff0c;普通函數(shù)內(nèi)部的this指向調(diào)用函數(shù)的對象(如果沒有對象調(diào)用,默認(rèn)為window);
- 構(gòu)造函數(shù)默認(rèn)的返回值是創(chuàng)建的對象(也就是實(shí)例),普通函數(shù)的返回值由return語句決定;
- 構(gòu)造函數(shù)的函數(shù)名與類名相同;
防抖和節(jié)流
防抖:當(dāng)持續(xù)觸發(fā)事件時(shí),一定時(shí)間段內(nèi)沒有再觸發(fā)事件,事件處理函數(shù)才會(huì)執(zhí)行一次,如果設(shè)定的時(shí)間到來之前,又一次觸發(fā)了事件,就重新開始延時(shí)。
節(jié)流:當(dāng)持續(xù)觸發(fā)事件時(shí),保證一定時(shí)間段內(nèi)只調(diào)用一次事件處理函數(shù)。
參考:js防抖和節(jié)流
代碼:
new 操作符做了什么
// 創(chuàng)建了一個(gè)空對象obj var obj = {}; // 將這個(gè)空對象的__proto__成員指向了Base函數(shù)對象prototype成員對象 obj.__proto__ = Base.prototype; // 將Base函數(shù)對象的this指針替換成obj,然后調(diào)用Base函數(shù) Base.call(obj);參考:js中的new()到底做了些什么??
websocket
websocket的特點(diǎn):
- 服務(wù)器可以主動(dòng)向客戶端推送信息,客戶端也可以主動(dòng)向服務(wù)器發(fā)送信息,是真正的雙向平等對話
- 建立在 TCP 協(xié)議之上,服務(wù)器端的實(shí)現(xiàn)比較容易
- 與 HTTP 協(xié)議有著良好的兼容性。默認(rèn)端口也是80和443,并且握手階段采用 HTTP 協(xié)議,因此握手時(shí)不容易屏蔽,能通過各種 HTTP 代理服務(wù)器
- 數(shù)據(jù)格式比較輕量,性能開銷小,通信高效
- 可以發(fā)送文本,也可以發(fā)送二進(jìn)制數(shù)據(jù)
- 有同源限制,客戶端可以與任意服務(wù)器通信
- 協(xié)議標(biāo)識(shí)符是ws(如果加密,則為wss),服務(wù)器網(wǎng)址就是 URL(ws://example.com:80/some/path)
WebSocket 是什么原理?為什么可以實(shí)現(xiàn)持久連接?
export default {name : 'websocket',data() {return {websock: null,}},created() {this.initWebSocket();},destroyed() {this.websock.close() //離開路由之后斷開websocket連接},methods: {initWebSocket(){ //初始化weosocketconst wsuri = "ws://127.0.0.1:8080";this.websock = new WebSocket(wsuri);this.websock.onmessage = this.websocketonmessage;this.websock.onopen = this.websocketonopen;this.websock.onerror = this.websocketonerror;this.websock.onclose = this.websocketclose;},websocketonopen(){ //連接建立之后執(zhí)行send方法發(fā)送數(shù)據(jù)let actions = {"test":"12345"};this.websocketsend(JSON.stringify(actions));},websocketonerror(){//連接建立失敗重連this.initWebSocket();},websocketonmessage(e){ //數(shù)據(jù)接收const redata = JSON.parse(e.data);},websocketsend(Data){//數(shù)據(jù)發(fā)送this.websock.send(Data);},websocketclose(e){ //關(guān)閉console.log('斷開連接',e);},}, }異步請求xhr、ajax、axios與fetch的區(qū)別比較
xhr:現(xiàn)代瀏覽器,最開始與服務(wù)器交換數(shù)據(jù),都是通過XMLHttpRequest對象。它可以使用JSON、XML、HTML和text文本等格式發(fā)送和接收數(shù)據(jù)。
- 優(yōu)點(diǎn):
不重新加載頁面的情況下更新網(wǎng)頁
在頁面已加載后從服務(wù)器請求/接收數(shù)據(jù)
在后臺(tái)向服務(wù)器發(fā)送數(shù)據(jù) - 缺點(diǎn):
使用起來也比較繁瑣,需要設(shè)置很多值
早期的IE瀏覽器有自己的實(shí)現(xiàn),這樣需要寫兼容代碼 - 代碼:
jQuery ajax:jQuery對XMLHttpRequest對象的封裝。
- 優(yōu)點(diǎn):
對原生XHR的封裝,做了兼容處理,簡化了使用。
增加了對JSONP的支持,可以簡單處理部分跨域。 - 缺點(diǎn):
如果有多個(gè)請求,并且有依賴關(guān)系的話,容易形成回調(diào)地獄。
本身是針對MVC的編程,不符合現(xiàn)在前端MVVM的浪潮。
ajax是jQuery中的一個(gè)方法。如果只是要使用ajax卻要引入整個(gè)jQuery非常的不合理。 - 代碼:
axios:Axios是一個(gè)基于promise的HTTP庫,可以用在瀏覽器和 node.js 中。它本質(zhì)也是對原生XMLHttpRequest的封裝,只不過它是Promise的實(shí)現(xiàn)版本,符合最新的ES規(guī)范。
- 優(yōu)點(diǎn):
從瀏覽器中創(chuàng)建XMLHttpRequests
從 node.js 創(chuàng)建 http 請求
支持 Promise API
攔截請求和響應(yīng)
轉(zhuǎn)換請求數(shù)據(jù)和響應(yīng)數(shù)據(jù)
取消請求
自動(dòng)轉(zhuǎn)換 JSON 數(shù)據(jù)
客戶端支持防御 XSRF - 缺點(diǎn):
只持現(xiàn)代代瀏覽器 - 代碼:
fetch:fetch是低層次的API,代替XHR,可以輕松處理各種格式,非文本化格式。可以很容易的被其他技術(shù)使用,例如Service Workers。但是想要很好的使用fetch,需要做一些封裝處理。
- 優(yōu)點(diǎn):
mode: 'no-cors’就可以跨域了 - 缺點(diǎn):
fetch只對網(wǎng)絡(luò)請求報(bào)錯(cuò),對400,500都當(dāng)做成功的請求,需要封裝去處理
fetch默認(rèn)不會(huì)帶cookie,需要添加配置項(xiàng)
fetch不支持abort,不支持超時(shí)控制,使用setTimeout及Promise.reject的實(shí)現(xiàn)超時(shí)控制并不能阻止請求過程繼續(xù)在后臺(tái)運(yùn)行,造成了流量的浪費(fèi)
fetch沒有辦法原生監(jiān)測請求的進(jìn)度,而XHR可以 - 代碼:
參考:異步請求xhr、ajax、axios與fetch的區(qū)別比較
對異步的理解
js是單線程的,一次只能做一件事情,遇到需要等待結(jié)果的任務(wù),如果一直等候,就會(huì)阻塞進(jìn)程,異步就是可以在某個(gè)任務(wù)等待結(jié)果的時(shí)候先執(zhí)行其他任務(wù),等結(jié)果返回后再執(zhí)行這個(gè)任務(wù)
總結(jié)
以上是生活随笔為你收集整理的前端面试总结--JS的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 请求转发、包含、重定向 getAt
- 下一篇: 前端面试JS三部分(三)