'這應該是最簡單的快速排序實現,什麼是快速排序呢?' '快速排序(Quicksort)是對冒泡排序的一種改進。 快速排序由C. A. R. Hoare在1962年提出。它的基本思想是: 經過一趟排序將要排序的數據分割成獨立的兩部分,其中一 部分的全部數據都比另一部分的全部數據都要小,而後再 按此方法對這兩部分數據分別進行快速排序,整個排序過程 能夠遞歸進行,以此達到整個數據變成有序序列。' function quickSort(arr) { if (arr.length <= 1) { return arr; } var left = [], right = [], baseDot = Math.round(arr.length / 2) base = arr.splice(baseDot, 1)[0]; for (var i = 0; i < arr.length; i++) { if (arr[i] < base) { left.push(arr[i]) } else { right.push(arr[i]) } } return quickSort(left).concat([base], quickSort(right)); } let arr = [5, 3, 4, 12]; const newarr = quickSort(arr); console.log(newarr); '每次遞歸調用,都會直到數組中只有一個數字爲止,而後 執行上下文棧出棧,返回上一個執行上下文循環遞歸,拼接數組'
'什麼是冒泡排序算法?' '冒泡排序(Bubble Sort),是一種計算機科學領域的較簡單的排序算法。 它重複地走訪過要排序的元素列,依次比較兩個相鄰的元素,若是他們的順 序(如從大到小、首字母從A到Z)錯誤就把他們交換過來。走訪元素的工做 是重複地進行直到沒有相鄰元素須要交換,也就是說該元素已經排序完成。 這個算法的名字由來是由於越大的元素會經由交換慢慢「浮」到數列的頂端 (升序或降序排列),就如同碳酸飲料中二氧化碳的氣泡最終會上浮到頂端 同樣,故名「冒泡排序」。' bubbleSortSoul1 = (arr = []) => { let count = 0; // i爲輪數(因i從0開始 即i<arr.length-1) for (let i = 0; i < arr.length - 1; i++) { count++; // 第i輪僅需比較length-1-i次 for (let j = 0; j < arr.length - 1 - i; j++) { // 這裏能不能寫成arr[j-1]>arr[j]? 若是有這種特殊癖好 那麼j就從1開始吧,而後j<arr.length-i if (arr[j] > arr[j + 1]) { let temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } console.log(`bubbleSortSoul1排序完成用了${count}輪`); return arr; }
'爲何咱們須要深度克隆?並且面試必問?'php
'由於引用數據類型存儲在堆空間中,當兩個變量同時指向同一個地址時, 只要一個改變,那麼另一個也會跟着變,咱們的原意是想一個改變,另 一個不變,那麼就須要從新開拓一個堆空間出來,因此就有了深度克隆。' '第一種方法(只適用於基礎類型)' const newObj = JSON.parse(JSON.stringify(oldObj)); '第二種方法,涵蓋全部的類型' const getType = (obj)=> { var toString = Object.prototype.toString; var map = { '[object Boolean]' : 'boolean', '[object Number]' : 'number', '[object String]' : 'string', '[object Function]' : 'function', '[object Array]' : 'array', '[object Date]' : 'date', '[object RegExp]' : 'regExp', '[object Undefined]': 'undefined', '[object Null]' : 'null', '[object Object]' : 'object', '[object Symbol]' : 'symbol' }; if(obj instanceof Element) {//由於對不一樣標籤,toString會返回對應不一樣標籤的構造函數 return 'element'; } return map[toString.call(obj)]; } const getRegExp = re => { var flags = ''; if (re.global) flags += 'g'; if (re.ignoreCase) flags += 'i'; if (re.multiline) flags += 'm'; return flags; }; /** * deep clone * @param {[type]} parent object 須要進行克隆的對象 * @return {[type]} 深克隆後的對象 */ const deepClone = oldObj => { // 維護兩個儲存循環引用的數組 const oldObjArr = []; const newObjArr = []; const clone = oldObj => { let newObj, proto; const type = getType(oldObj); switch(type){ case 'boolean': case 'number': case 'string': case 'null': case 'undefined': case 'function':{ return oldObj; break; } case 'symbol':{ return Symbol(Symbol.keyFor(oldObj).toString()); break; } case 'array':{ newObj = []; break; } case 'regExp':{ newObj = new RegExp(oldObj.source, getRegExp(oldObj)); if (oldObj.lastIndex) newObj.lastIndex = oldObj.lastIndex; break; } case 'date':{ newObj = new Date(oldObj.getTime()); break; } //case 'obj': default:{ // 處理對象原型 proto = Object.getPrototypeOf(oldObj); // 利用Object.create切斷原型鏈 newObj = Object.create(proto); break; } } // 處理循環引用 const index = oldObjArr.indexOf(oldObj); if (index != -1) {// 若是父數組存在本對象,說明以前已經被引用過,直接返回此對象 return newObjArr[index]; } oldObjArr.push(oldObj); newObjArr.push(newObj); /*數組和對象均可以用forin語句,雖然數組使用forin會有一個問題(具體看最下面)。 可是這裏不會影響,因此這麼用 */ for (let i in oldObj) {// 遞歸 newObj[i] = clone(oldObj[i]); } return newObj; }; return clone(oldObj); } /* 測試成功 */ function person(pname) { this.name = pname; } const Messi = new person('Messi'); function say() { console.log('hi'); }; const oldObj = { a: say, b: new Array(1), c: new RegExp('ab+c', 'i'), d: Messi }; const newObj = deepClone(oldObj); console.log(newObj.a, oldObj.a); //[Function: say] [Function: say] console.log(newObj.b[0], oldObj.b[0]); // undefined undefined console.log(newObj.c, oldObj.c); // /ab+c/i /ab+c/i console.log(newObj.d.constructor, oldObj.d.constructor); // [Function: person][Function: person] '全部的類型均可以被克隆,完美版'
const obj = { name: 'json', age: 1, friend: '夢露', info: { name: 'Aron', age: 2, friend: '傑西卡', info: { name: 'rose', age: 3, friend: '黴黴', info: { name: 'jerry', age: 4, friend: '比伯', info: { name: 'damu', age: 5, friend: 'XJ', }, }, }, } } let namearr, agearr, friendarr; namearr = []; agearr = []; friendarr = []; check(obj); function check(obj) { const items = Object.getOwnPropertyNames(obj) items.forEach(function (item) { if (Object.prototype.toString.call(obj[item]) == '[object Object]') { check(obj[item]); } else { if (item.toString() === 'name') { namearr.push(obj[item]) } else if (item.toString() === 'age') { agearr.push(obj[item]) } else if (item.toString() === 'friend') { friendarr.push(obj[item]) } } }) } /* 這種方法也是同樣的效果 使用for in循環代替的Object.getOwnPropertyNames(obj)方法 function check(obj) { for (var item in obj) { if (Object.prototype.toString.call(obj[item]) == '[object Object]') { check(obj[item]); } else { if (item == 'name') { namearr.push(obj[item]) } else if (item == 'age') { agearr.push(obj[item]) } else if (item == 'friend') { friendarr.push(obj[item]) } } } }*/ console.log(`namearr:${namearr}`) console.log(`agearr:${agearr}`) console.log(`friendarr:${friendarr}`)
let arr = [1, 1, 2, 2, 5, 5, 'a', 'a', '3', '3'] arr = arr.sort(); let realarr = []; for (let i = 0; i < arr.length; i++) { if (i == 0) { realarr.push(arr[i]) } else if (i !== 0 && arr[i] !== arr[i - 1]) { realarr.push(arr[i]) } } console.log(realarr)
### 如何將一個對象深度凍結?html
跟上面的數組面試題同樣,利用了執行上下文棧,先進的後出,最早凍結最深層裏面的那個屬性, 再依次返回上一層繼續凍結 var obj = { name:"王寶強", age:18, wife:{ name:"陳羽凡", age:17, son:{ name:"賈乃亮", age:48, girlFriend:{ name:"吳秀波", age:50, zuo:function () { console.log("翻舊帳") }, foods:["棉花糖","粉色的棉花糖","各類棉花糖",{a:"a"}] } } } }; Object.prototype.deepFreeze = function () { var keys = Object.getOwnPropertyNames(this); var that = this; keys.forEach(function (key) { var val = that[key]; if(Object.prototype.toString.call(val) === "[object Object]" || Object.prototype.toString.call(val) === "[object Array]"){ val.deepFreeze() } }); return Object.freeze(this) } obj.deepFreeze()
## 請使用定時器和canvas寫一個隨機生成多個彩色泡前端
<html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> * { margin: 0; padding: 0; } body, html { width: 100%; height: 100%; overflow: hidden; } canvas { position: absolute; top: 0; left: 0; bottom: 0; right: 0; margin: auto; border: 1px solid; background: white; } </style> </head> <body> <canvas width="400" height="400"></canvas> </body> <script> window.onload = function () { var canvasnode = document.querySelector("canvas"); var arr=[]; if (canvasnode.getContext) { var ctx = canvasnode.getContext("2d"); } setInterval(function(){ console.log(arr) ctx.clearRect(0,0,canvasnode.width,canvasnode.height) arr.forEach(function(item,index){ item.r++; item.opa-=0.01; if(item.opa<=0){ arr.splice(index,1) } } ) arr.forEach(function(item){ ctx.save() ctx.fillStyle="rgba("+item.red+","+item.green+","+item.blue+","+item.opa+")"; ctx.beginPath() ctx.arc(item.x,item.y,item.r,0,2*Math.PI) ctx.fill() ctx.restore() } ) } ,10) setInterval(function(){ var obj={x:0,y:0,r:0,red:0,green:0,blue:0,opa:0}; obj.x=Math.random()*400; obj.y=Math.random()*400; obj.r=10; obj.opa=1; obj.red =Math.round(Math.random()*255); obj.green =Math.round(Math.random()*255); obj.blue =Math.round(Math.random()*255); arr.push(obj); },100 ) } </script> </html>
(function(w){ w.app = {}; w.app.getElementByClassName=function(className){ var allnode=document.getElementsByTagName("*"); console.log(allnode) var arr=[]; for(var i=0;i<allnode.length;i++){ var newclass=" "+allnode[i].className+" "; var reg=new RegExp("\\s+"+className+"\\s+","i"); if(reg.test(newclass)){ arr.push(allnode[i]); } } return arr } }(window)
'因爲ajax通常用於比較舊的技術,這裏不適用ES6語法' var xhr = new XMLHttpRuest(); xhr.onreadystatechange = function () { if (xhr.readyState == 0) { //xhr對象建立好了 初始化狀態 console.log(0) } if (xhr.readyState == 1) { //表明xhr.send方法還未調用(還未發送請求),還能夠設置請求頭相關信息 console.log(1) } if (xhr.readyState == 2) { //此時send方法被調用了 響應頭和首行已經回來了 console.log(xhr.getResponseHeader('etag')) console.log(2) } if (xhr.readyState == 3) { console.log(3) } if (xhr.readyState === 4 && xhr.status === 200) { console.log(4) console.log(xhr.responseText); } } xhr.open('GET', 'http://localhost:3000/ajax?username=123&password=456'); // xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded'); // xhr.send('username=123&password=456'); xhr.send() }*/ '//上面是原生的ajax寫法,下面是jQuery中的兩種ajax寫法' ' 1. 原生jQuery寫法 ' $('#btn').click(function () { $.ajax({ url: 'http://localhost:3000/ajax', data: 'username=jack&password=123', method: 'POST', success: function (data) { console.log(data) }, error: function (error) { console.log(error) } }) '2.簡寫' $.post('http://localhost:3000/ajax', 'username=rose&age=12', (data) => { console.log(data) }) '//若是是get請求直接上面改爲get就能夠了,data是服務器響應回來的數據'
const express = require('express'); const app = express(); app.use(express.static('public')) app.use(express.urlencoded({ extended: true })); app.get('/ajax', (req, res) => { console.log(req.query) res.send('這是ajax的get請求') }) app.post('/ajax', (req, res) => { res.send('這是ajax的post請求') console.log(req.body) }) app.listen(3000, err => { if (!err) { console.log('端口監聽成功'); } else { console.log('端口監聽失敗' + err); } })
'這裏使用了遞歸,還有Object原型上的方法,執行上下文棧先進後出的知識。' const obj = { name: 'json', age: 1, friend: '夢露', info: { name: 'Aron', age: 2, friend: '傑西卡', info: { name: 'rose', age: 3, friend: '黴黴', info: { name: 'jerry', age: 4, friend: '比伯', info: { name: 'damu', age: 5, friend: 'XJ', }, }, }, } } let namearr, agearr, friendarr; namearr = []; agearr = []; friendarr = []; check(obj); /* function check(obj) { const items = Object.getOwnPropertyNames(obj) items.forEach(function (item) { if (Object.prototype.toString.call(obj[item]) == '[object Object]') { check(obj[item]); } else { if (item.toString() === 'name') { namearr.push(obj[item]) } else if (item.toString() === 'age') { agearr.push(obj[item]) } else if (item.toString() === 'friend') { friendarr.push(obj[item]) } } }) }*/ function check(obj) { for (var item in obj) { if (Object.prototype.toString.call(obj[item]) == '[object Object]') { check(obj[item]); } else { if (item == 'name') { namearr.push(obj[item]) } else if (item == 'age') { agearr.push(obj[item]) } else if (item == 'friend') { friendarr.push(obj[item]) } } } } console.log(`namearr:${namearr}`) console.log(`namearr:${agearr}`) console.log(`namearr:${friendarr}`)
## 請手寫一個jsonp和cors 解決跨域問題的代碼 ?java
'jsonp' document.getElementById('btn').onclick = function () { /* 1. jsonp - 特色: 1. 利用script標籤自然跨域跨域的特性解決跨域的, 民間推出的 2. 兼容性極好 */ //建立一個script標籤 const script = document.createElement('script'); //設置了響應成功的回調函數 window.jsoncallback = function (data) { console.log(data); } //設置script的src屬性, 向指定服務器發送請求 script.src = 'http://localhost:3000/?callback=jsoncallback'; //添加到body中生效 document.body.appendChild(script); } ------ 'cors的解決方法:在Node.js的服務器代碼中設置一個響應頭' app.get('/cors', (req, res) => { /* 1. cors 特色: - 官方推出解決跨域的方案,使用起來及其簡單,只需在服務器設置一個響應頭 - 兼容性較差 */ //設置響應頭 res.set('access-control-allow-origin', '*'); //容許全部網址跨域
'1.瀏覽器的事件輪詢機制 瀏覽器中對於js依靠js引擎實現,js引擎是單線程,不像java,php這些能夠是多線程,高併發。若是要說到瀏覽器的輪詢機制,那麼咱們首先要說的 就是單線程的js引擎,前端的核心編程思惟模式是異步編程,不管是頁面效果、先後端的數據交互,都是以異步爲核心,每一個須要異步的場景, 每每伴隨着回調函數去執行,而單線程的JS引擎是沒法自身作這麼多工做,還須要異步線程。 1.每當JS引擎解析代碼時遇到異步代碼時,交給異步線程,繼續往下解析代碼。 2.異步線程處理這些異步代碼時,一旦他們的所對應的回調函數達到執行條件便會塞進異步隊列中,等待JS引擎的輪詢。 3.JS引擎會在解析完下面的全部代碼後,再去輪詢異步隊列,從左到右,依次執行,這也是說爲何定時器的時間不許確的緣由,在JS 解析代碼時,若是遇到下面代碼特別多的時候,那麼它就沒時間去輪詢異步隊列的代碼。 瀏覽器中的輪詢機制有一個特殊的 requestAnimationFrame(callbackname),它所對應的回調函數,是在瀏覽器下一次重繪重排時執行,它是一個宏任務,有待考證 ,目前看只要觸發重繪重排就會調用回調函數,能夠避免掉幀,優化性能,減小重繪重排次數,即便一個空白頁面,它也會重繪重排,因此只要運用好, 它是徹底能夠替代定時器,還可使用cancelAnimationFrame(callbackname)清除。 '
'Node.js的事件輪詢機制外還有同步代碼,微任務, 要想完全弄懂Node的代碼執行,得結合下面的微任 務一塊兒學習。' '1.執行已經到時間的setTimeout 和 setInterval 2.執行延遲到一個循環迭代的i/o回調 3.系統內部的 idle prepare等 4.poll 輪詢回調隊列,依次取出,同步執行,與JS的異步隊列執行有點相像 直到回調隊列爲空 或者系統奔潰了 若是回調隊列沒有內容,那麼看 以前是否設置過setImmadiate(),若是有就去下一個階段,若是沒有,就在當前等待新的回調函數。 若是定時器的時間到了,那麼也會去下一個階段 5. setImmediate 6.關閉的回調函數 ,一些準備關閉的函數等. Node.js的事件輪詢機制也能夠當作是單線程,由上往下執行,可是到了第6階段,又會返回第一階段,死循環。 '
'想得太多反而很差,把每一個宏任務當作銀行排隊的老大爺,把微任務當作老大爺須要的業務, 可能他須要辦存款,理財,買記念幣等,櫃檯人員不幫老大爺辦完 他所須要的任務 -- 微任務,就不會切換到下一個老大爺 -- 宏任務, 可是編程的邏輯不 能徹底抽象成現實生活, 照這種說法,只能先有老大爺纔會有業務須要, 。但是在Node中,先執行的是微任務,只有微任務若是有多層,先執行最頂層,再往下依次執 行)執行完後才能去執行宏任務,微任務有兩種,一種是process.nextTick() 中的函數,一種是Promise.then()中的函數,只有他們執行完後,纔會去執行宏任務:setTim eout ,setIneterval,setImmadie。(即執行完了微任務纔會遵循Node.js的輪詢機制去執行, 一切微任務優先)'