大家好,我是若川。最近組織了源碼共讀活動,感興趣的可以加我微信?ruochuan12
若川視野原意是若川的前端視野。但太長了就留下了四個字,不知道的以為關注的不是技術公眾號。今天分享一篇慕課網精英講師河畔一角的好文章~
廢話不多說,上貨!
手寫Promise
這道題說實話已經老掉牙了,但是還是有不少公司會問,而且還有相當一部分前端寫不出來
function?Promise(callback){const?pending?=?'pending';const?fulfilled?=?'fulfilled';const?rejected?=?'rejected';//?當前狀態this.state?=?pending;//?當前值this.value?=?null;//?失敗原因this.reason?=?null;//?成功和失敗數組對象this.fulfilledCallback?=?[];this.rejectedCallback?=?[];//?成功處理this.resolve?=?(data)=>{setTimeout(()=>{if(this.state?==?pending){this.state?=?fulfilled;this.value?=?data;this.fulfilledCallback.map(fn=>fn(this.value));}})}//?失敗處理this.reject?=?(reason)=>{setTimeout(()=>{if(this.state?==?pending){this.state?=?rejected;this.reason?=?reason;this.rejectedCallback.map(fn=>fn(this.reason));}})}//?捕獲成功和失敗,扔到成功和失敗數組this.then?=?function(succesFn,errorFn){this.fulfilledCallback.push(succesFn);this.rejectedCallback.push(errorFn);}//?捕獲異常,直接扔到異常數組中this.catch?=?(errorFn)=>{this.rejectedCallback.push(errorFn);}//?默認需要執行一次resolve和rejectcallback(this.resolve,this.reject);
}//?驗證結果
new?Promise((resolve,reject)=>{setTimeout(()=>{resolve(10);},1000)
}).then((data)=>{console.log(data);
},(error)=>{console.log(error);
})
注意事項:
Promise要暴露then/catch方法
Promise構造函數接收一個立即執行的函數callback
then/catch只負責把回調放入數組即可
resolve/reject負責執行
resolve/reject 需要添加宏任務(setTimeout)
compose組合函數實現
后一個函數作為前一個函數的參數
最后一個函數可以接受多個參數,前面的函數只能接受單個參數;后一個的返回值傳給前一個
//?Demo:
const?add?=?num?=>?num??+?10
const?multiply?=?num?=>?num?*?2
const?foo?=?compose(multiply,?add)
foo(5)?=>?30
//?聚合函數
export?default?function?compose(...funcs)?{//?如果是空,直接返回空函數并接受一個參數if?(funcs.length?===?0)?{return?arg?=>?arg}//?如果有一個,直接執行并返回結果if?(funcs.length?===?1)?{return?funcs[0]}//?如果對reduce不了解,可以先去看下技術文章return?funcs.reduce((a,?b)?=>?(...args)?=>?a(b(...args)))
}
注意事項:
compose是一個聚合函數
compose執行后返回一個函數(所以這就是為什么當func.length==0的時候,要return一個箭頭函數)
reduce始終返回一個箭頭函數,后一個函數先執行并把結果作為前一個函數的參數,依次進行
數組柯里化Curry(求和)
阿里面試題
實現如下效果:
sum(1,3).sumOf()??4
sum(1,3)(2,4).sumOf()?10
function?sum(){var?arr?=?[].slice.apply(arguments);var?fn?=?function(){arr?=?arr.concat([].slice.apply(arguments))return?fn;}fn.sumOf?=?function(){return??arr.reduce((total,num)=>total+num,0);}return?fn;
}
實現一個LazyPig
阿里面試題
實現一個LazyPig,可以按照以下方式調用:
LazyPig("Peggy")?
//?輸出:
>?Hello,I'm Peggy!
LazyPig("Peggy").sleep(10).eat("dinner")
//?輸出
>?Hello,I'm Peggy!
//等待10秒..
Wake?up?after?10
Eat?dinner~
function?LazyPig(name){console.log(`Hello,I'm?${name}`)var?fn?=?{}fn.sleep?=?function(time){console.log(`Wake?up?${time}`)let?start?=?Date.now()while(Date.now()-start<=time){}console.log(`Wake?up?down`)return?fn;}fn.eat?=?function(food){console.log(`Eat?${food}`)return?fn;}return?fn;
}
數組扁平化
let?list?=?[1,?5,?[9,?8],?[2,?[1,?9]],?7];
//?第一種方法:
console.log(list.toString().split(','));//?第二種方法:
function?flatten(list)?{return?list.reduce((prev,?item)?=>?{return?prev.concat(Array.isArray(item)???flatten(item)?:?item);},?[])
}
console.log(flatten(list));
對象扁平化
/**
*?對象扁平化
*?說明:請實現 flatten(input)?函數,input 為一個 javascript 對象(Object 或者 Array),返回值為扁平化后的結果。
*?示例:
*?var?input?=?{
*?a:?1,
*?b:?[?1,?2,?{?c:?true?},?[?3?]?],
*?d:?{?e:?2,?f:?3?},
*?g:?null,
*?}
*?var?output?=?flatten(input);
*?output如下
*?{
*?"a":?1,
*?"b[0]":?1,
*?"b[1]":?2,
*?"b[2].c":?true,
*?"b[3][0]":?3,
*?"d.e":?2,
*?"d.f":?3,
*?//?"g":?null,?值為null或者undefined,丟棄
*?}
*/
解答:
```js
let?result?=?{};
var?flatten?=?(data,?key)?=>?{if?(data?instanceof?Array)?{data.forEach((param,?index)?=>?{if?(param?instanceof?Object)?{flatten(param,?`${key}[${index}]`);}?else?{result[`${key}[${index}]`]?=?param;}});}?else?if?(data?instanceof?Object)?{for?(var?itemKey?in?data)?{const?itemValue?=?data[itemKey];if?(itemValue?instanceof?Object)?{flatten(itemValue,?itemKey);}?else?if?(itemValue)?{if?(key)?{result[`${key}.${itemKey}`]?=?flatten(itemValue,?itemKey);}?else?{result[itemKey]?=?itemValue;}}}}?else?{return?data;}
};
flatten(input);
console.log(result)
數組轉換為Tree
//?數組轉換成Tree
var?list?=?[{id:?1,?name:?'jack',?pid:?0},{id:?2,?name:?'jack',?pid:?1},{id:?3,?name:?'jack',?pid:?1},{id:?4,?name:?'jack',?pid:?2},
]????const?getTree?=?(root,?result,?pid)?=>?{for?(let?i?=?0;?i?<?root.length;?i++)?{if?(root[i].pid?==?pid)?{let?item?=?{?...root[i],?children:?[]?}result.push(item)getTree(root,?item.children,?item.id)}}
}let?array?=?[];
getTree(list,?array,?0)
console.log(JSON.stringify(array))
對象深拷貝
//?對象深度克隆
let?obj?=?{name:?'jack',age:?10,fn:?()?=>?{return?this.name;},list:?['fe',?'node',?'small'],all:?{child:?true}
}
//?方法一:(面試官不想要)
JSON.parse(JSON.stringify(obj))
//?方法二:
function?deepClone(obj){let?result;if(typeof?obj?===?'object'){result?=?Array.isArray(obj)???[]?:?{}for(let?i?in?obj){result[i]?=?typeof?obj[i]?===?'object'???deepClone(obj[i]):obj[i];}}else{result?=?obj;}return?result;
}
貪心算法(找零)
商店老板有1、2、5、10面額的紙幣,小伙買東西給了100花了80,計算如何找零是最佳(阿里面試題)
function?MinCoinChange(coins)?{return?function(amount)?{let?total?=?0,?change?=?[]for(let?i=?coins.length;?i>=0;?i--)?{let?coin?=?coins[i]while(total?+?coin?<=?amount)?{change.push(coin)total?+=?coin}}return?change}
}MinCoinChange([1,2,5,10])(20)返回:10,10
數組去重(兩次以上去重)
常規去重大家都知道Set
//?已知數組
var?arr?=?[1,1,1,1,1,1,1,3,3,3,3,3,5,5];
//?方法一
function?delRepeat(){arr?=?arr.sort();//先排序for(let?i=0;i<arr.length;i++){if(arr[i]?==?arr[i+2]){arr.splice(i,1);i--;}}return?arr;
}
//?方法二
function?delRepeat(){var?newArr?=?[];var?obj?=?{};arr.map(item=>{if(obj[item]){obj[item]?+=1?;}else{obj[item]?=?1;}obj[item]<=2?newArr.push(item):''})return?newArr;
}
大數相加算法
大數相加有很多考題形式,有整數、小數、平方根等(騰訊考題)
已知:let a = "12345.12123",b="987654.92";
function?sum(a,b){let?arr1?=?a.split(''),arr2?=?b.split('');let?count?=?0;let?arr?=?[];let?a1?=?a.split('.')[1],b1?=?b.split('.')[1];let?len?=?a1.length?-?b1.length;if(len>0)arr2.push(’0’.repeat(len))if(len<0)arr1.push(’0’.repeat(Math.abs(len)))while(arr1.length?||?arr2.length){let?m?=?arr1.pop()?||?0,n?=?arr2.pop()?||?0;if(m?!=?'.'){let?num?=?Number(m)?+?Number(n)?+?count;if(num?>?9){count?=?1;num%=10;}else{count?=?0;}?arr.unshift(num);}else{arr.unshift('.');}}if(count>0)arr.unshift(count);let?res?=?arr.join('');console.log(res);
}
如果是正整數,BigInt會更方便處理
二叉樹求和
var?treenode?=?{value:?1,left:?{value:?2,left:?{value:?4,},right:?{value:?5,left:?{value:?7,},right:?{value:?8,},},},right:?{value:?3,right:?{value:?6,},},
}function?sum(root)?{let?list?=?[]if?(root)?list.push(root.value);if?(root.left)?{list?=?list.concat(sum(root.left));}if?(root.right)?{list?=?list.concat(sum(root.right));}return?list;
}
console.log(sum(treenode));
爬樓梯
/*
假設你正在爬樓梯。需要 n 階你才能到達樓頂。
每次你可以爬 1 或 2 個臺階。你有多少種不同的方法可以爬到樓頂呢?
思路:
f(1)?:?1
f(2)?:?11?,?2
f(3)?:?12,?111,?21
f(4)?:?121,?1111,?211,?112,?22
f(n)?=?f(n-1)?+?f(n-2)
*/
function?fn(n)?{if?(n?==?1)?return?1;if?(n?==?2)?return?2;return?fn(n?-?1)?+?fn(n?-?2);
}
console.log(fn(4))
簡易模板引擎
const?template?=?'嗨,{{?info.name.value?}}您好,今天是星期?{{?day.value?}}';const?data?=?{info:?{name:?{value:?'張三'}},day:?{value:?'三'}
};function?render(template,?data)?{return?template.replace(/{{\s+?([\w.]+)\s+?}}/g,?function?($0,?$1)?{return?eval(`data.${$1}`)})
}const?result?=?render(template,?data);?
//?嗨,張三您好,今天是星期三
console.log(result)
前端模擬并發請求
已知當前有100個請求,每次只能同時調用5個,設計一個并發函數。
function?send(){//?初始化數組let?list?=?Array.from({length:100}).map((k,i)=>i)//?最大并發次數const?limit?=?5;//定義異步函數const?asyncGet?=async?(item)=>{return?item;}//?初始化100個異步請求函數,當閉包被執行的時候會執行一個請求,當請求執行完后,會獲取下一個并執行const?arr?=?[]const?handlers?=?()=>{list?=?list.map(item=>{return?()=>{return?asyncGet(item).then((res)=>{console.log('res:'+res)let?next?=?list.shift();if(next){next()}else{console.log('全部執行完成')}})}})}handlers();//?一次性取出最大并發數并執行for(let?i=0;i<limit;i++){let?fn?=?list.shift();arr.push(fn())}Promise.all(arr).then((res)=>{})
}send();
防抖和節流(最經典的閉包案例)
/*
防抖:
定義:規定時間內,只觸發一次,如果規定時間內再次調用,會清空繼續創建新的任務。
場景:widow.onresize或者onscroll,或者搜索框
*/
function?debounce(fn,?wait)?{var?timeout?=?null;return?function()?{if(timeout?!==?null){clearTimeout(timeout);timeout?=?setTimeout(fn,?wait);}else{timeout?=?setTimeout(fn,?wait);}}
}
window.addEventListener('scroll',?debounce(()=>{
//?TO-DO
},?500));
/*
節流:固定時間內,只觸發一次。
場景:搜索框
*/
function?throttle(fn,delay){let?valid?=?truereturn?function()?{if(!valid){//休息時間?暫不接客return?false?}//?工作時間,執行函數并且在間隔期內把狀態位設為無效valid?=?falsesetTimeout(()?=>?{fn()valid?=?true;},?delay)}
}
以上就是本次給大家整理分享的前端算法面試題,絕不是從網上隨意找的,很多都是我經歷過的以及朋友面試的,希望對大家有幫助。
最近組建了一個江西人的前端交流群,如果你是江西人可以加我微信?ruochuan12?私信 江西?拉你進群。
推薦閱讀
1個月,200+人,一起讀了4周源碼
我歷時3年才寫了10余篇源碼文章,但收獲了100w+閱讀
老姚淺談:怎么學JavaScript?
我在阿里招前端,該怎么幫你(可進面試群)
·················?若川簡介?·················
你好,我是若川,畢業于江西高校。現在是一名前端開發“工程師”。寫有《學習源碼整體架構系列
從2014年起,每年都會寫一篇年度總結,已經寫了7篇,點擊查看年度總結。
同時,最近組織了源碼共讀活動
識別上方二維碼加我微信、拉你進源碼共讀群
今日話題
略。歡迎分享、收藏、點贊、在看我的公眾號文章~