'这应该是最简单的快速排序实现,什么是快速排序呢?' '快速排序(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的轮询机制去执行, 一切微任务优先)'