02-JDBC事務支援
寫在前面
最近我終於找到了一份滿意的工作.準備面試的過程中,我整理出了一些有用的筆記,這篇就是其中之一.
既然寫好了,不妨就放在這裡分享給大家.
面試通常都有現場寫程式碼的題目,我基本每次都或多或少的翻車.有意思的是,每次面試結束,自己改不到五分鐘就調試出來了.
所以面試中的寫程式碼的過程,一定不能緊張,要沉住氣慢慢來.只要不是系統自動檢查結果,只要是面試官看著你寫,就有很大的表現的機會,哪怕最後做不出來.
我參加的最煩人的面試,是那種系統判定結果的面試,只要做不出來,就絕對不可能通過.
仔細想想,在二三十場面試中,很少有我完整寫出毫無瑕疵的答案的題目,但基本也都順利通過了.
對比來看,我的同學中,答案能執行成功但面試沒通過的也大有人在.
可以肯定的是,面試官不會只根據是否能執行成功來評價應聘者.
所以,只需要順著正確思路穩穩地做就好了,不要怕最後執行不成功.
如果實在做不出,也一定要和麵試官說清你當前的進展和思路,而不是一句"我不會"就想結束問題.
當然,我指的只是前端這個對演算法能力要求不強的崗位…
有一場百度的面試我甚至直接和麵試官說,我是個偏感性的人,喜歡但是不擅長做演算法.都給我通過了.
總的來看,如果能完全掌握這篇文章的內容,就足以應付所有前端面試中的手撕程式碼環節了.
來都來了,點個讚唄
轉載請註明出處
導讀
關於這篇文章,有幾點我想先說清楚,方便讀者更順利的學習.
這篇文章不適合前端小白閱讀,需要對
JS
和ES6
有一定了解,否則遇到一些寫法可能不太看得懂因為精力有限,我只加了較為粗略但足以幫助讀者理解的註釋,因為多數題也只有幾行程式碼而已.
如果遇到還不懂的地方,我認為讀者完全可以自己去查詢文件來了解為什麼這麼做,為什麼使用這個函式.
或者,先查詢該問題通常的解決思路,再回來參考我的實現
程式碼大量使用了
ES6
的語法學習手撕程式碼,不只是理解的過程,更是實踐的過程
我在完全掌握(可以默寫出每段程式碼,並講清楚每一行的作用)以下程式碼的過程中,做了以下幾件事
參考別人的實現,結合自己的思路,寫出一個自己的版本
不斷對程式碼進行優化
當你嘗試去優化一段程式碼的時候,對它的理解和記憶會異常深刻
不看之前的實現,重新自己實現一次
再和之前的實現做對比,檢查錯誤
反覆閱讀和默寫,直到可以完全正確的默寫為之
作為一個專業的程式設計師,除了工作中的編碼,額外的無實際產出的練習(反覆練習解決一個問題,反覆默寫同一段程式碼),也是必不可少的.
這就像歌手不可能到了舞臺上才去練習自己的聲音.他一定會在平時大量去練聲.
這就是我強調要反覆敲程式碼的原因.別想著平時只要理解,工作中再去熟能生巧.
工作不是給你練習的地方,工作是你的舞臺.
下文中幾乎每一段程式碼,都是我反覆優化後的結果,希望可以帶給讀者新的啟發.
我把程式碼大致分成了幾個專題,一共包含了大致30個問題的解決方案
除了文章中的問題,還有些我沒有提到的,都是頻率較低的問題
關於演算法題,除了排序和查詢我也基本沒有寫.因為演算法問題千變萬化,需要的是解決問題的思維,而不是固定的實現
重要性與順序無關
有問題可以問我,我都會回覆
目錄
DOM
事件代理
document.getElementById("#father-id").onclick=function(event){
event=ev||window.event
let target=ev.target||ev.srcElement
//可以自己列印一下event.target.nodeName,看看是什麼
if (target.nodeName.toLowerCase()==='xxx'){
//事件內容
}
}
陣列 物件
扁平化
function flatten(arr) {
let result=[]
for (let i=0,len=arr.length;i<len;i++) {
if (Array.isArray(arr[i])) {
result=result.concat(flatten(arr[i]))
} else {
result.push(arr[i])
}
}
return result
}
去重 - unique()
function unique(arr) {
let appeard=new Set()
return arr.filter(item=>{
//建立一個可以唯一標識物件的字串id
let id=item+JSON.stringify(item)
if (appeard.has(id)) {
return false
} else {
appeard.add(id)
return true
}
})
}
拷貝
淺拷貝
function copy(obj) {
let result=Array.isArray(obj)?[]:{}
Object.keys(obj).forEach(key=>result[key]=obj[key])
return result
}
otherStar={...star}
Object.assign({},star)
深拷貝
copy()
函式實現
處理了迴圈引用和key為symbol型別的情況
function copy(obj,appeard=new Map()) {
if (!(obj instanceof Object)) return obj//如果是原始資料型別
if (appeard.has(obj)) return appeard.get(obj)//如果已經出現過
let result=Array.isArray(obj)?[]:{}
appeard.set(obj,result)//將新物件放入map
//遍歷所有屬性進行遞迴拷貝
;[...Object.keys(obj),...Object.getOwnPropertySymbols(obj)]
.forEach(key=>result[key]=copy(obj[key],appeard))
return result
}
JSON.stringify
- 只能處理純JSON資料
- 有幾種情況會發生錯誤
- 包含不能轉成 JSON 格式的資料
- 迴圈引用
- undefined,NaN, -Infinity, Infinity 都會被轉化成null
- RegExp/函式不會拷貝
- new Date()會被轉成字串
new=JSON.parse(JSON.stringify(old))
字串
去除空格 - trim()
function myTrim(str) {
return str.replace(/(^\s+)|(\s+$)/g,'')//將前空格和後空格替換為空
}
function myTrim(str) {//記錄前後空格的個數,最後對字串進行擷取
let first=0,last=str.length
for (let i in str) {
if (str[i]===' ') {
first++
} else {
break
}
}
for (let i=last;i>first;i--) {
if (str[i]===' ') {
last--
} else {
break
}
}
return str.substr(first,last-first)
}
字串全排列
廣度優先實現
function combine(str) {//抽出一個字元s,對其餘的進行排列,將s放在每種排列開頭
if (str.length===1) return [str]
let results=[]
for (let i in str) {
for (let s of combine(str.slice(0,i)+str.slice(1+(+i)))) {
results.push(str[i]+s)
}
}
//可能會出現類似"aa"=>[aa,aa,aa,aa]的情況,需要去重
return [...new Set(results)]
}
深度優先實現
function combine(str) {//記錄已經使用過的字元,深度優先訪問所有方案
let result=[]
;(function _combine(str,path=''){
if (str.length===0) return result.push(path)
for (let i in str) {
_combine(str.slice(0,i)+str.slice((+i)+1,str.length),path+str[i])
}
})(str)
//可能會出現類似"aa"=>[aa,aa,aa,aa]的情況,需要去重
return [...new Set(result)]
}
排序和查詢
插入排序
function sort(arr) {//原地
for (let i in arr) {//選一個元素
while (i>0&&arr[i]<arr[i-1]) {//向前移動到合適的位置
[arr[i],arr[i-1]]=[arr[i-1],arr[i]]
i--
}
}
}
歸併排序
function sort(arr) {
if (arr.length===1) return arr
//分成兩部分
let mid=Math.floor(arr.length/2)
let [part1,part2]=[sort(arr.slice(0,mid)),sort(arr.slice(mid))]
//對比+合併
let result=[]
while (part1.length>0&&part2.length>0)
result.push((part1[0]<part2[0]?part1:part2).shift())
return [...result,...part1,...part2]
}
快速排序
function sort(arr) {
if (arr.length<=1) return arr
//選基準值
let mid_pos=arr.length>>1
let mid=arr.splice(mid_pos,1)[0]
let left=[],right=[]
//和基準值比較,分別插入left,right陣列
arr.forEach(item=>(item<=mid?left:right).push(item))
return [...sort(left),mid,...sort(right)]//遞迴呼叫排序
}
二分查詢
function search(arr,target) {//迴圈寫法,不斷移動左右指標,縮小範圍
let left=0,right=arr.length-1
while (left<=right) {
const mid_pos=Math.floor((left+right)/2)
const mid_val=arr[mid_pos]
if (target===mid_val) {
return mid_pos
} else if (target>mid_val) {
left=mid_pos+1
} else {
right=mid_pos-1
}
}
return -1
}
找出出現次數最多的元素 - getMostItem()
function getMost(arr) {
//計數
let map=new Map()
arr.forEach(item=>{
if (map.has(item)) {
map.set(item,map.get(item)+1)
} else {
map.set(item,1)
}
})
//找出出現最多
let [max_vals,max_num]=[[arr[0]],map.get(arr[0])]
map.forEach((count,item)=>{
if (count>max_num){
max_vals=[item]
max_num=count
} else {
max_vals.push(item)
}
})
return max_vals
}
console.log(getMost(['1', '2', '3', '3', '55', '3', '55', '55']))
功能函式實現
setTimeout
實現setInterval
function myInterval(fn,interval,...args) {
let context=this
setTimeout(()=>{
fn.apply(context,args)
myInterval(fn,interval,...args)//別忘了為它傳入引數
},interval)
}
myInterval((num)=>console.log(num),500,10)
函式柯里化
function sum(...args1){
return function (...args2) {
return [...args1,...args2].reduce((p,n)=>p+n)
}
}
console.log(sum(1, 2, 2)(7))
防抖 節流
實現了兩個加工方法,返回一個加工後的防抖/節流函式
防抖
function debounce(fn,delay) {
let timer=null
return function (){
if (timer) clearTimeout(timer)
timer=setTimeout(()=>fn.call(...arguments),delay)//別忘了為它傳入引數
}
}
節流
function throttle(fn,delay) {
let flag=true
return function() {
if (!flag) return
flag=false
setTimeout(()=>{
fn(...arguments)//別忘了為它傳入引數
flag=true
},delay)
}
}
資料結構
單鏈表
function Node(element) {//結點類
[this.element,this.next]=[element,null]
}
class LinkList {//連結串列類
constructor() {
this.length=0
this.head=new Node()
this.tail=new Node()
this.head.next=this.tail
}
get_all() {
let result=[]
let now=this.head
while (now.next!==this.tail) {
now=now.next
result.push(now.element)
}
return result
}
unshift(element) {//開頭新增
let node=new Node(element)
node.next=this.head.next
this.head.next=node
}
shift(){//開頭刪除
let node=this.head.next
this.head.next=this.head.next.next
return node.element
}
}
let list=new LinkList()
list.unshift(15)
list.unshift(16)
list.unshift(17)
console.log(list.shift())//17
console.log(list.get_all())//[ 16, 15 ]
設計模式
釋出訂閱模式
class Observer {
constructor() {
this.events={}//事件中心
}
publish(eventName,...args) {//釋出=>呼叫事件中心中對應的函式
if (this.events[eventName])
this.events[eventName].forEach(cb=>cb.apply(this,args))
}
subscribe(eventName,callback) {//訂閱=>向事件中心中新增事件
if (this.events[eventName]) {
this.events[eventName].push(callback)
} else {
this.events[eventName]=[callback]
}
}
unSubscribe(eventName,callback) {//取消訂閱
if (events[eventName])
events[eventName]=events[eventName].filter(cb=>cb!==callback)
}
}
JS原生API實現
bind()
call()
apply()
apply()
Function.prototype.myApply=function(context,args) {
context.fn=this//為context設定函式屬性
let result=context.fn(...args)//呼叫函式
delete context.fn//刪除context的函式屬性
return result
}
call()
//除了...args
//和apply都一樣
Function.prototype.myCall=function(context,...args) {
context.fn=this
let result=context.fn(...args)
delete context.fn
return result
}
bind()
Function.prototype.myBind=function(context,args1) {//使用[閉包+apply]實現
return (...args2)=>this.apply(context,[...args1,...args2]);
}
InstanceOf
function myInstanceOf(son,father) {//沿著父親的原型鏈向上查詢是否有兒子的原型
while (true) {
son=son.__proto__
if (!son) return false
if (son===father.prototype) return true
}
}
myInstanceOf([], Array) // true
new
function myNew(constructor_fn,...args) {
//構造新的空物件
let new_obj={}
new_obj.__proto__=constructor_fn.prototype
let result=constructor_fn.apply(new_obj,args)
//如果建構函式沒有返回一個物件,則返回新建立的物件
//如果建構函式返回了一個物件,則返回那個物件
//如果建構函式返回原始值,則當作沒有返回物件
return result instanceof Object?result:new_obj
}
function Animal(name) {
this.name = name;
}
let animal = myNew(Animal, 'dog');
console.log(animal.name) // dog
reduce()
forEach()
reduce()
api用法:
arr.reduce(function(prev, cur, index, arr){}, initialValue)
實現:
Array.prototype.myReduce=function(fn,init_val){
let [val,idx]=init_val?[init_val,0]:[this[0],1]//設定初始值
for (let i=idx,len=this.length;i<len;i++) {
val=fn(val,this[i],i,this)//迴圈並迭代結果
}
return val
}
console.log([1,2,3,4,5].reduce((pre,item)=>pre+item,0)) // 15
forEach()
api用法:
[1,3,5,7,9].myForEach(function(item,index,arr) {
console.log(this)
},15)
實現:
Array.prototype.myForEach=function(fn,temp_this) {
for (let i=0,len=this.length;i<len;i++){
fn.call(temp_this,this[i],i,this)//迴圈陣列元素,為回撥函式傳入引數
}
}
Promise
Promise.all()
Promise.prototype.all=function(promiseList) {
return new Promise((resolve,reject)=>{
if (promiseList.length===0) return resolve([])
let result=[],count=0
promiseList.forEach((promise,index)=>{
Promise.resolve(promise).then(value=>{
result[index]=value
if (++count===promiseList.length) resolve(result)
},reason=>reject(reason))
})
})
}
ES6所有API完整實現
通過Promise/A+ test測試
實現細節過多,還請參照Promise/A+規範閱讀
也可以直接參考我關於promise的筆記
深入理解promise
https://blog.csdn.net/weixin_43758603/article/details/109641486
class Promise {
constructor(task) {
this.status="pending"
this.value=undefined
this.reason=undefined
this.fulfilled_callbacks=[]
this.rejected_callbacks=[]
try {
task(this._resolve,this._reject)
} catch (error) {
this._reject(error)
}
}
then(onFulfilled,onRejected){
if (this.status==='fulfilled') {
let promise2=new Promise((resolve,reject)=>{
setTimeout(()=>{
try {
if (!this._isFunction(onFulfilled)) {
resolve(this.value)
} else {
this._resolvePromise(promise2,onFulfilled(this.value))
}
} catch (error) {
reject(error)
}
},0)
})
return promise2
} else if (this.status==='rejected') {
let promise2=new Promise((resolve,reject)=>{
setTimeout(()=>{
try {
if (!this._isFunction(onRejected)) {
reject(this.reason)
} else {
this._resolvePromise(promise2,onRejected(this.reason))
}
} catch (error) {
reject(error)
}
},0)
})
return promise2
} else if (this.status==='pending') {
let promise2=new Promise((resolve,reject)=>{
this.fulfilled_callbacks.push(()=>{
try {
if (!this._isFunction(onFulfilled)) {
resolve(this.value)
} else {
this._resolvePromise(promise2,onFulfilled(this.value))
}
} catch (error) {
reject(error)
}
})
this.rejected_callbacks.push(()=>{
try {
if (!this._isFunction(onRejected)) {
reject(this.reason)
} else {
this._resolvePromise(promise2,onRejected(this.reason))
}
} catch (error) {
reject(error)
}
})
})
return promise2
}
}
catch=onRejected=>this.then(null,onRejected)
finally=onFinished=>this.then(onFinished,onFinished)
static deferred(){
let deferred={}
deferred.promise=new Promise((resolve,reject)=>{
deferred.resolve=resolve
deferred.reject=reject
})
return deferred
}
static resolve(value) {
if (value instanceof Promise) return value
return new Promise(resolve=>resolve(value))
}
static reject=reason=>{return new Promise((resolve, reject)=>reject(reason))}
static all(promiseList) {
return new Promise((resolve,reject)=>{
if (promiseList.length===0) return resolve([])
let result=[],count=0
promiseList.forEach((promise,index)=>{
Promise.resolve(promise).then(value=>{
result[index]=value
if (++count===promiseList.length) resolve(result)
},reason=>reject(reason))
})
})
}
static race(promiseList) {
return new Promise((resolve,reject)=>{
if (promiseList.length===0) return resolve()
promiseList.forEach(promise=>{
Promise.resolve(promise)
.then(value=>resolve(value),reason=>reject(reason))
})
})
}
static allSettled(promiseList) {
return new Promise(resolve=>{
let result=[],count=0
if (len===0) return resolve(result)
promiseList.forEach((promise,i)=>{
Promise.resolve(promise).then(value=>{
result[i]={
status:'fulfilled',
value:value
}
if (++count===promiseList.length) resolve(result)
},reason=>{
result[i]={
status:'rejected',
reason:reason
}
if (++count===promiseList.length) resolve(result)
})
})
})
}
_resolve=value=>{
if (this.status!=='pending') return
setTimeout(()=>{
this.status ='fulfilled'
this.value = value
this.fulfilled_callbacks.forEach(cb=>cb(this.value))
},0)
}
_reject=reason=>{
if (this.status!=='pending') return
setTimeout(()=>{
this.reason = reason
this.status ='rejected'
this.rejected_callbacks.forEach(cb=>cb(this.reason))
},0)
}
_isFunction=f=>Object.prototype.toString.call(f).toLocaleLowerCase()==='[object function]'
_isObject=o=>Object.prototype.toString.call(o).toLocaleLowerCase()==='[object object]'
_resolvePromise(promise,x){
if (promise===x) {
promise._reject(new TypeError('cant be the same'))
return
}
if (x instanceof Promise) {
if (x.status==='fulfilled') {
promise._resolve(x.value)
} else if (x.status==='rejected') {
promise._reject(x.reason)
} else if (x.status==='pending') {
x.then(value=>{
this._resolvePromise(promise,value)
},reason=>{
promise._reject(reason)
})
}
return
}
if (this._isObject(x)||this._isFunction(x)) {
let then
try {
then=x.then
} catch (error) {
promise._reject(error)
return
}
if (this._isFunction(then)) {
let called=false
try {
then.call(x,value=>{
if (called) return
called=true
this._resolvePromise(promise,value)
},reason=>{
if (called) return
called=true
promise._reject(reason)
})
} catch (error) {
if (called) return
promise._reject(error)
}
} else {
promise._resolve(x)
}
} else {
promise._resolve(x)
}
}
}
module.exports = Promise
HTTP請求
AJAX封裝
function ajax(method,url,params,callback) {
//對引數進行處理
method=method.toUpperCase()
let post_params=null
let get_params=''
if (method==='GET') {
if (typeof params==='object') {
let tempArr=[]
for (let key in params) {
tempArr.push(`${key}=${params[key]}`)
}
params=tempArr.join('&')
}
get_params=`?${params}`
} else {
post_params=params
}
//發請求
let xhr=new XMLHttpRequest()
xhr.onreadystatechange=function(){
if (xhr.readyState!==4) return
callback(xhr.responseText)
}
xhr.open(method,url+get_params,false)
if (method==='POST')
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')
xhr.send(post_params)
}
ajax('get','https://www.baidu.com',{id:15},data=>console.log(data))
JSONP
function jsonp(url, params_obj, callback) {
//建立一個供後端返回資料呼叫的函式名
let funcName = 'jsonp_' + Data.now() + Math.random().toString().substr(2, 5)
//將引數拼接成字串
if (typeof params==='object') {
let temp=[]
for (let key in params) {
temp.push(`${key}=${params[key]}`)
}
params=temp.join('&')
}
//在html中插入<script>資源請求標籤
let script=document.createElement('script')
script.src=`${url}?${params}&callback=${funcName}`
document.body.appendChild(script)
//在本地設定供後端返回資料時呼叫的函式
window[funcName]=data=>{
callback(data)
delete window[funcName]
document.body.removeChild(script)
}
}
//使用方法
jsonp('http://xxxxxxxx',{id:123},data=>{
//獲取資料後的操作
})
js插入html中標籤的內容
<script src="https://www.liuzhuocheng.com?callback=funcName"></script>
後端返回的<script>
資源的內容
<script src="https://www.liuzhuocheng.com?callback=funcName">
funcName('datadatadatadatadatadatadatadata')
</script>