写这篇文章的目的是在于最近在看一些面试题之类的(实在是菜的不行啊,想着没事干的时候就刷题吧,感觉自己不能再放任下去了),但是到处搜集答案看完以后当时懂了,后面就又忘记了,想要再次回顾的话就会比较麻烦,所以就把掘金当成笔记本了,以后想看也会比较方便,有兴趣的小伙伴可以一起哦。
编程题
1.实现一个简单的模板字符串替换
let str='{{name}}很厉害,才{{age}}岁';
let context={name:'小明',age:18};
str.replace(/\{\{(.*?)\}\}/g,(match,key)=>context[key.trim()]);正则小知识点:
- 修饰符:i,g,m
- 方括号:用于查找某个范围内的字符
- 元字符:是拥有特殊含义的字符,比如\w代表查找单词字符,/W代表查找非单词字符
- 量词:类似于n+,代表匹配任何包含至少一个 n 的字符串
2.将数组扁平化并取出其中重复数据,最终得到一个升序且不重复的数组
法一:
let arr=[1, 1,[3, 2, [4, 5]]];
Array.from(new Set(arr.flat(3))).sort(function(a,b){
return a-b;
});法二:
let arr=[1,1, [3, 2, [4, 5]]];
Array.from(new Set(arr.toString().split(','))).sort(function(a,b){
return a-b;
})知识点:
- Array.from()
把set对象变为普通数组
- new Set
创建set对象,用于数组去重
- flat
扁平化方法
- arr.toString()结果位"1,3,2,4,5";
- sort 方法
其中关于为什么return a-b是升序的问题可参考文章这篇文章:www.cnblogs.com/saifei/p/90…
参考:www.cnblogs.com/wind-lanyan…
3.介绍下深度优先遍历和广度优先遍历,如何实现?
深度优先遍历实现:
法一(递归法):
let testObj = {
name: 1,
children: [
{ name: 2, children: [{ name: 3, children: [{ name: 4 }] }] },
{ name: 5, children: [{ name: 6 }] }
]
};
function dfs(obj,result=[]){
result.push(obj.name);
if(obj.children&&obj.children.length){
for(let i=0;i<obj.children.length;i++){
dfs(obj.children[i],result)
}
return result;
}
}
dfs(testObj);法二(栈写法):
let testObj = {
name: 1,
children: [
{ name: 2, children: [{ name: 3, children: [{ name: 4 }] }] },
{ name: 5, children: [{ name: 6 }] }
]
};
function dfs(obj) {
let squese = [],
result = [];
squese.push(obj);
while (squese.length) {
let obj1 = squese.pop();
result.push(obj1.name);
if (obj1.children) {
for (let j = obj1.children.length-1; j >= 0; j--) {
squese.push(obj1.children[j]);
}
}
}
return result;
}
dfs(testObj);
广度优先遍历实现:
let testObj =
{ name: 1,
children: [
{ name: 2, children: [{ name: 3, children: [{ name: 4 }] }] },
{ name: 5, children: [{ name: 6 }] }
]
};
function bfs(obj){
let result=[],squ=[];
squ.push(obj);
while(squ.length){
[...squ].forEach((item)=>{
squ.shift();
result.push(item.name)
if(item.children){
squ.push(...item.children)
}
})
}
return result;
}
bfs(testObj);这一题可以参考www.cnblogs.com/zzsdream/p/…和blog.csdn.net/weixin_3042…
4.请分别用深度优先思想和广度优先思想实现一个拷贝函数?
深度优先思想实现拷贝:
let testObj =
{ name: 1,
children: [
{ name: 2, children: [{ name: 3, children: [{ name: 4 }] }] },
{ name: 5, children: [{ name: 6 }] }
]
};
function getType(obj){
return Object.prototype.toString.call(obj).slice(8,-1);
}
function dfsCopy(obj,arr=[]){
let copyObj={};
if(getType(obj)=='Object'||getType(obj)=='Array'){
if(~arr.indexOf(obj)){
copyObj=arr.indexOf(obj)
}else{
arr.push(obj)
for(let item in obj){
copyObj[item]=dfsCopy(obj[item],arr)
} }
}else if(getType(obj)=='Function'){
copyObj=eval('('+obj.toString()+')')
}else{
copyObj=obj;
}
return copyObj;
}
dfsCopy(testObj);
也可这样(只考虑的object喝array类型):
function cloneDeep(target,map = new WeakMap()) {
if(typeOf taret ==='object'){
let cloneTarget = Array.isArray(target) ? [] : {};
if(map.get(target)) {
return target;
}
map.set(target, cloneTarget);
for(const key in target){
cloneTarget[key] = cloneDeep(target[key], map);
}
return cloneTarget
}else{
return target
}
}可参考:https://juejin.cn/post/6844904200917221389
广度优先思想拷贝:后续分享。。。。
5.如何实现一个new?
思路:
function Person(name,age){
this.name=name;
this.age=age;
};
function newObj(){
let obj={};//创建一个对象
let constructor=[].shift.call(arguments);//获取第一个参数Person,也就是构造函数
obj._proto_=constructor.prototype;//使对象能访问Person.prototype的属性
constructor.apply(obj,arguments);//是对象能访问构造函数Person的属性
return obj
}
newObj(Person,'小明',1)参考文章:
实现new :
[].shift.call(arguments):
calll,applay:
prototype和_proto_:
6.请把[A1,A2,B1,B2 ,C1,C2,D1,D2]和[A,B,C,D]合并为[A1,A2,B1,B2 ,C1,C2,D1,D2]?
let a=['A1','A2','B1','B2' ,'C1','C2','D1','D2'],b=['A','B','C','D'];
let ans=[...a,...b].sort((a1,a2)=>(
a1.codePointAt(0)-a2.codePointAt(0)||a2.length-a1.length
||a1.codePointAt(1)-a2.codePointAt(1)
))
注:ES6 提供了codePointAt()方法,能够正确处理 4 个字节储存的字符,返回一个字符的码点,codePointAt()方法的参数,是字符在字符串中的位置(从 0 开始);
其中‘a’.codePointAt(1)==undefined;
7.改造下面的代码,使之输出0-9,写出你能想到的所有解法?
这里就不上代码了,方法太多,可以看这篇文章blog.csdn.net/weixin_4487…
其中涉及到对Object.create(null)的了解:blog.csdn.net/seafishyls/…
8.使用迭代的方式实现flatten函数?
如:let arr = [1, 2, [3, 4, 5, [6, 7], 8], 9, 10, [11, [12, 13]]]
法一:
let arr = [1, 2, [3, 4, 5, [6, 7], 8], 9, 10, [11, [12, 13]]];
function flatten(arr){
let newArr=[];
function flat(arr1){
for(let item of arr1){
if(Array.isArray(item)){
flat(item)
}else{
newArr.push(item);
}
}
}
flat(arr);
return newArr;
}
flatten(arr);
法二:
let arr = [1, 2, [3, 4, 5, [6, 7], 8], 9, 10, [11, [12, 13]]];
function flatten(arr){
while(arr.some(item=>Array.isArray(item))){
arr=[].concat(...arr);
}
return arr;
}
flatten(arr);法三:
let arr = [1, 2, [3, 4, 5, [6, 7], 8], 9, 10, [11, [12, 13]]];
function flatten(arr){
return arr.reduce((pre,cur)=>Array.isArray(cur)?[...pre,...flatten(cur)]:[...pre,cur],[])
}
flatten(arr);9.var a=?; if(a==1&&a==2&&a==3){console.log(1)},什么情况下会打印出1?
let a={
i:1,
valueOf:function(){
return this.i++;
}
}
if(a==1&&a==2&&a==3){
console.log(1);
}参考文章:zhuanlan.zhihu.com/p/110086149和segmentfault.com/a/119000001…
特例:
10.实现一个sleep函数,比如sleep(1000)意味着等待1000毫秒,可从Promise、Generator、Async、Await等角度实现?
法一:
function sleep(ms){
let dateNow=new Date().getTime();
while(new Date().getTime()-dateNow<=ms){
continue;
}
}
function test(){
console.log(1);
sleep(1000);
console.log(2)
}
test();法二:
function sleep(ms,callback){
setTimeout(callback,ms)
}
sleep(1000,()=>{
console.log(1)
})法三:
function sleep(ms){
return new Promise((resolve)=>
setTimeout(resolve,ms)
)
}
sleep(1000).then(()=>{console.log(1)})法三:
function sleep(ms){
return new Promise((resolve)=>{
setTimeout(resolve,ms)
})
}
async function test(ms){
let res =await sleep(ms);
console.log('test')
return res;
}
test(1000);function sleep(ms){
yield new Promise(function(resolve,rejecte){
setTimeout(resolve,ms)
})
}
sleep(1000).next().value.then(()=>{console.log(1)})
11.实现(5).add(3).minus(2)功能?
例:5+3-2,结果为6
Number.prototype.add=function(num){
return this.valueOf()+num
}
Number.prototype.minus=function(num){
return this.valueOf()-num
}
console.log((5).add(3).minus(2));12.冒泡排序如何实现,时间复杂度是多少,还可以如何改进?
改进后的冒泡排序:
function mySort(arr){
for(let i=0;i<arr.length;i++){
let flag=true;
for(let j=0;j<arr.length-i-1;j++){
if(arr[j]>arr[j+1]){
flag=false;
let t=arr[j]
arr[j]=arr[j+1];
arr[j+1]=t;
}
}
if(flag==true){
break;
}
}
return arr;
}
mySort([2,4,6,3,5,8,1])这里我们加入了标志,flag,如果有一趟排序中没有产生交换的话,那么说明此刻数列以及变成了有序的数列
注比较次数是6+5+4+3+2+1 也就是n(n-1)/2,因此时间复杂度O(N^2)
时间复杂度应是O(n^2),但改进后使最佳情况时为O(n)(顺序不需要改变的情况)
13.某公司1到12月份的销售额存在一个对象里
如下:{1:222,2:123,5:888},请把数据处理为如下结构:
[222,123,null,null,888,null,null,null,null,null,null,null]
法一:
function myFunc(obj){
let arr=[];
for(let i=0;i<12;i++){
if(obj.hasOwnProperty(i+1)){
arr.push(obj[i+1])
}else{
arr.push(null);
}
}
return arr;
}
myFunc({1:222,2:123,5:888})法二:
Array.from({length:12}).map((item,index)=>{return obj[index+1]?obj[index+1]:null})
//或者
Array.from({length:12},(item,index)=>{return obj[index+1]?(obj[index+1]):null})
Array.from() 可以通过以下方式来创建数组对象:
- 伪数组对象(拥有一个
length属性和若干索引属性的任意对象) - 可迭代对象(可以获取对象中的元素,如 Map和 Set 等)
Array.from() 方法有一个可选参数 mapFn,让你可以在最后生成的数组上再执行一次 map 方法后再返回。也就是说 Array.from(obj, mapFn, thisArg) 就相当于 Array.from(obj).map(mapFn, thisArg)
14.要求设计lazyMan类,实现以下功能?
例:
LazyMan('Tony');
// Hi I am Tony
LazyMan('Tony').sleep(10).eat('lunch');
// Hi I am Tony // 等待了10秒...
// I am eating lunch LazyMan('Tony').eat('lunch').sleep(10).eat('dinner');
// Hi I am Tony // I am eating lunch
// 等待了10秒... // I am eating diner
LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food');
// Hi I am Tony
// 等待了5秒...
// I am eating lunch
// I am eating dinner
// 等待了10秒...
// I am eating junk food参考:建议www.jianshu.com/p/f1b7cb456…和 muyiy.vip/question/pr…一起看
class tClass{
constructor(name){
this.taskList=[];
this.name=name;
console.log('Hi I am '+name);
setTimeout(()=>{
this.next()//这个必须有,否则不会继续执行
},0)
}
eat(str){
const fn=()=>{
console.log('I am eating '+str);
this.next()
}
this.taskList.push(fn)
return this;
}
sleepFirst(ts){
const fn=()=>{setTimeout(()=>{
this.next();
},ts*1000)
}
this.taskList.unshift(fn);//需要在eat之前执行,因此unshift到eat放入之前
return this;
}sleep(ts){
const fn=()=>{setTimeout(()=>{
this.next();
},ts*1000)
}
this.taskList.push(fn)
return this;
} next(){
const fn=this.taskList.shift();
fn&&fn();
}
}
function lazyMan(name){ return new tClass(name)};
lazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food')
思路:函数按照执行顺序放入taskList数组,执行的时候按照队列的方法先进先出。
15.给定两个数组,写一个方法来计算它们的交集?
例如:给定nums1=[1,2,2,1],nums2=[2,2],返回[2,2]
答案:参考segmentfault.com/a/119000001…
function intersect(nums1,nums2){
let pos1=0;
let pos2=0;
let nums3=[];
while(pos1<nums1.length&&pos2<nums2.length){
if(nums1[pos1]==nums2[pos2]){
nums3.push(nums1[pos1]);
pos1++;
pos2++;
}else if(nums1[pos1]<nums2[pos2]){
pos1++;
}else{
pos2++;
}
}
return nums3;
}
intersect([1,2,2,1],[2,2]);
16.如何设计实现无缝轮播?
17.随机生成一个长度为10的整数类型的数组,例如[2,10,3,4,5,11,10,11,20],将其排列成一个新数组,要求新数组形式如下,列入[[2,3,4,5],[10,11],[20]]?
参考:www.jianshu.com/p/823a2f2f0…
理解一:把现有数组按区间来分,以10位为单位区分,比如10以内的一组,10-20之间的一组,20-30之间的一组,以此类推;
let arr=[2,10,3,4,5,11,10,11,20];
function changeArr(arr){
let result=[];
arr.forEach((item,index)=>{
let key=parseInt(item/10);
if(Array.isArray(result[key])&&!result[key].includes(item)){
result[key].push(item);
}else{
result[key]=[];
result[key].push(item)
}
})
return result;
}
changeArr(arr);理解二:把现有数组如果是连续的放一组,不连续的单独放
let arr=[2,10,3,4,5,11,10,11,20];
function changeArr(arr){
let newArr=arr.sort((x,y)=>{
return x-y;
})
let result=[];
let j=0;
for(let i=0;i<newArr.length;i++){
while(i<arr.length-1&&(newArr[i]+1==newArr[i+1]||newArr[i]==newArr[i+1])){
i+=1;
}
result.push(newArr.slice(j,i+1))
j=i+1;
}
result=result.map((item)=>{
return Array.from(new Set(item))
})
return result;
}
changeArr(arr);18.如何把一个字符串的大小写取反,(大写变小写,小写变大写),例如‘AbC’编程‘aBc'?
'abCd'.replace(/[a-zA-Z]/g,function(a){
return /[a-z]/.test(a)?a.toUpperCase():a.toLowerCase()
})19.实现一个字符串匹配算法,从长度为n的字符串S中,查找是否存在字符串T,T的长度是m,若存在返回所在的位置?
function find(S,T){
if(S.length<T.length) return -1;
for(let i=0;i<S.length;i++){
if(S.slice(i,i+T.length)==T){
return i;
}
}
}
find('abcde','cd');20.使用JavaScript Proxy实现简单数据绑定?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>proxy</title>
</head>
<body>
<h1>使用Proxy 和 Reflect 实现双向数据绑定</h1>
<input type="text" id="input">
<h2>您输入的内容是: <i id="txt"></i></h2>
<script>
//获取dom元素
let oInput = document.getElementById("input");
let oTxt = document.getElementById("txt");
//初始化代理对象
let obj = {};
//给obj增加代理对象
let newProxy = new Proxy(obj, {
get: (target, key, recevier) => {
return Reflect.get(target, key, recevier);
},
set: (target, key, value, recevier) => {
//监听newProxy是否有新的变化
if (key == "text") {
oTxt.innerHTML = value;
}
//将变化反射回原有对象
return Reflect.set(target, key, value, recevier);
}
})
//监听input输入事件
oInput.addEventListener("keyup", (e) => {
//修改代理对象的值
newProxy.text = e.target.value;
})
</script>
</body>
</html>
21.旋转数组算法题?
给定一个数组,将数组中的元素向右一定k个位置,其中k是非负数
//法一:
let arr=[1,2,3,4,5,6];
function test(arr,k){
let k1=k%arr.length;
return arr.slice(arr.length-k1).concat(arr.slice(0,arr.length-k1));
}
test(arr,2);//法二:
var rotate = function(nums,k) {
for(var i = 0;i<k;i++){
nums.unshift(nums.pop());
};
return nums;
};
rotate([1,2,3,4,5,6],2)22.打印出1-10000之间的所有对称数 例如121,1331等?
笨方法。。。。:
function test(num){
let result=[];
for(let k=11;k<=num;k++){
let i=k.toString();
let len=i.length/2;
let arr1=i.slice(0,len);
let arr2=i.slice(len);
if(i.length%2==1){
arr2=arr2.slice(1)
}
arr2=arr2.split('').reverse().join('');
if(arr2==arr1){
result.push(i)
}
}
return result;
}
test(10000)法二:
function test(num){
let len=num.toString().length/2;
let len1=+((num+'').slice(0,len))+1
let result=[];
for(let i=1;i<=len1;i++){
let a=i.toString()+i.toString().split('').reverse().join('');
if(+a<=num){
result.push(a)
}
for(let k=0;k<10;k++){
let b=i.toString()+k.toString()+i.toString().split('').reverse().join('');
if(+b<=num){
result.push(b);
}
}
}
return result;
}
test(10000)23.算法题(移动0),给定一个数组nums,编写一个函数将所有的0移动到数组的末尾,同时保持非0元素的相对顺序?
说明:
必须在原数组上啊哦做,不能拷贝额外的数组
尽量减少操作次数
function test(nums){
let len=nums.length;
let j=0;
for(let i=0;i<len-j;i++){
if(nums[i]==0){
nums.push(0);
nums.splice(i,1);
i--;
j++;
}
}
return nums;
}
test([1,2,3,0,4,0,0,5])
24.实现一个add函数,满足以下功能?
add(1);//1
add(1)(2);//3
add(1)(2)(3);//6
add(1)(2,3);//6
add(1,2)(3);//6
add(1,2,3);//6function add(){
let args=[].slice.apply(arguments);
function fn(){
return add(...args.concat([].slice.apply(arguments)));
//或者 return add.apply(null,args.concat([].sice.apply(arguments)))
}
fn.toString=function(){ //或者重写fn.valueOf方法也能改变函数的隐式转换结果
return args.reduce((x,y)=>{
return x+y;
})
}
return fn;
};
add(1);//1
add(1)(2);//3
add(1)(2)(3);//6
add(1)(2,3);//6
add(1,2)(3);//6
add(1,2,3);//6ps:找了很多关于这个题的文章,以下这篇大神写的比较详细易懂
并且尤其是这句解决了我关于为什么要使用add.apply的疑虑
25.算法题之两数之和?
给定一个整数数组和一个目标值,找出数字居中和为目标值的两个数。
你可以假定每个输入只对应一种答案,且同样的元素不能被重复利用。
法一:
//一趟哈希表:
function test(nums,target){
let map={};
for(let i=0;i<nums.length;i++){
if(target-nums[i] in map){
return [target-nums[i],nums[i]]
}else{
map[nums[i]]=i;
}
}
}
test([2,3,5,4,7],7)法二:两次for循环
26.在输入框如何判断输入的是一个正确的网址?
function isUrl(url){
try{
new URL(url);
return ture;
}catch(err){
return false;
}
}
27.实现convert方法,把原始list转换成树形结构,要求尽可能降低时间复杂度?
参考文章blog.csdn.net/u012193330/…
28.实现模糊搜素的关键词高亮显示?
<!DOCTYPE html>
<html lang="z-cn">
<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>Search highlight</title>
<style>
b {
color: red;
}
li {
list-style: none;
}
</style>
</head>
<body>
<input id="ipt" type="text" />
<section>
<ul id="container"></ul>
</section>
</body>
<script>
const container = document.getElementById("container");
const ipt = document.getElementById("ipt");
//节流
function throttle(_func_) {
let run = true;
return function() {
if (!run) return;
run = false;
setTimeout(() => {
_func_.apply(this, arguments);
run = true;
}, 300);
};
}
//缓存,并且必须是一个闭包,不然没法缓存
function memorize(fn) {
const cache = new Map();
return (name) => {
if (!name) {
container.innerHTML = "";
return;
}
if (cache.get(name)) {
container.innerHTML = cache.get(name);
return;
}
//没有缓存过则处理模板字符串并且添加缓存
const res = fn.call(fn, name).join("");
cache.set(name, res);
container.innerHTML = res;
};
}
function handleInput(value) {
const reg = new RegExp(`\(${value}\)`);
const search = data.reduce((res, cur) => {
if (reg.test(cur)) {
//以下两个正常匹配参考https://www.cnblogs.com/xzdm/p/12566114.html
const match = RegExp.$1;
res.push(`<li>${cur.replace(match, "<b>$&</b>")}</li>`);
}
return res;
}, []);
return search;
}
const data = [
"济南大明湖",
"济南大明湖公园",
"济南大明湖饭店",
"济南大酒店",
"济南趵突泉"
];
const memorizeInput = memorize(handleInput);
ipt.addEventListener(
"input",
throttle((e) => {
memorizeInput(e.target.value);
})
);
</script>
</html>正则知识点参考:www.cnblogs.com/xzdm/p/1256…
全文参考:www.jianshu.com/p/693cfc7ac…
29.已知数据格式,实现一个fn找出链条中所有的父级id?
const data2 = [{ id: '1', name: 'test1', children: [ { id: '11', name: 'test11', children: [ { id: '111', name: 'test111' }, { id: '112', name: 'test112' } ] }, { id: '12', name: 'test12', children: [ { id: '121', name: 'test121' }, { id: '122', name: 'test122' } ] } ]}];
function test(data,value){
let res=[];
function dfs(arr,temp=[]){
for(let i=0;i<arr.length;i++){
if(arr[i].children){
temp.push(arr[i].id)
dfs(arr[i].children,temp)
}else{
if(arr[i].id==value){
res=temp;
return;
}
}
}
}
dfs(data);
return res
}
test(data2,122);30.给定两个大小为m和n的有序数组nums1和nums2。请找出这两个有序数组的中位数。要求算法的 时间复杂度为O(log(m+n))?
//示例1:
nums1=[1,3];
nums2=[2]
//中位数是2.0
示例2:
nums1=[1,2];
nums2=[3,4];
//中位数是(2+3)/2=2.5
let nums1=[1,2],nums2=[3,4];
function test(nums1,nums2){
for(let i=0;i<nums2.length;i++){
nums1.push(nums2[i])
}
nums1.sort((a,b)=>{
return a-b
})
if(nums1.length%2==0){
return (nums1[nums1.length/2]+nums1[nums1.length/2-1])/2
}else{
return nums1[nums1.length-1/2]
}
}
test(nums1,nums2);哈哈,终于更新到第30个题了,还有30个题要更新,先复习几天前面的题喽
31.模拟实现一个深拷贝,并考虑对象相互引用以及Symbol拷贝的情况?