综合面试题-js深入版
-
数组扁平化
一维数组:数组中只有一级
二维数组:数组中有两级
多维数组:数组中有两级及以上
变成一维数组的操作——扁平化
-
flat
es6中新增的方法
ps.不知道的api找mdn
去重
-
对象键值对
-
新开一个数组 for循环 indexOf includes来判断
-
new Set来去重
-
……
经典算法
-
toString
思路:将数组变为字符串
toString是会把一个数组直接变为一个字符串,字符串里绝对不会出现中括号[],而把每项用逗号, 分隔;
toString会自动扁平化,以逗号连接
这里扩展一下Object.prototype.toString.call({name:'angela',age:18})
Object.prototype.toString() - JavaScript | MDN
join也是一样的
ps.map改变每项的值,返回啥就改成啥
-
JSON.stringify
stringify和toString的区别在于 stringify是有中括号的
let arr=[1,2,[3,8],[35,87,[26,9]]]
let arr1=[{name:'angela',age:18},{name:'tom',age:18}]
// 1.flat方法
console.log('1.flat方法--------------------')
console.log(arr.flat(Infinity));
// 2.toString和join
console.log('2.toString和join--------------------')
console.log(arr.toString().split(',').map(i=>Number(i)));
console.log(arr.join().split(',').map(i=>Number(i))); // join同toString是一样
console.log(arr1.flat(Infinity));
console.log(arr1.toString().split(',')); // 有局限的
console.log(arr.join('|').split(/,|\|/g)); // 有局限的
// 3.JSON.stringify
console.log('3.JSON.stringify--------------------')
console.log(JSON.stringify(arr).replace(/\[|\]/g,'').split(','))
console.log(JSON.stringify(arr1).replace(/\[|\]/g,''));
-
concat(...array)
前言:
用函数制定一个规则,检测数组中的某一项是否符合这个规则
some做检测
ps.forEach map some find
Array.isArray手动改原型链依然判为false 检测数据类型
...arr:每次只展开一级,展开不了很深
// 4.concat(...arr)
console.log('4.concat(...arr)--------------------')
console.log([].concat(...arr))
while(arr.some(i=>Array.isArray(i))){
arr=[].concat(...arr)
}
console.log(arr);
-
递归处理
递归思路:
fn执行的时候又调了自己
循环当前这个数组的每一项:
-
不是数组,创建一个新数组存起来;
-
当前是数组,再调这个方法(先不管这个),重新进来
// res放在外面
let res=[]
function deep(arr){
for(let i=0;i<arr.length;i++){
if(arr[i] instanceof Array){
deep(arr[i]);
continue;
}
res.push(arr[i])
}
}
let arr=[1,2,[3,8],[35,87,[26,9]]]
deep(arr);
console.log(res);
// res放在里面
function myFlatter(arr){
let res=[]
arr.forEach(i=>{
if(Array.isArray(i)){
res=res.concat(myFlatter(i))
}else{
res.push(i)
}
})
return res;
}
res=myFlatter(arr);
console.log(res);
function flatter(arr){
return arr.reduce((total,i)=>{
// if(Array.isArray(i)){
// total=total.concat(flatter(i));
// }else{
// total.push(i)
// }
// return total;
return Array.isArray(i)?total.concat(flatter(i)):[...total,i]
},[])
}
res=flatter(arr);
console.log(res);
JavaScript数组扁平化的五种方式_二九君的博客-CSDN博客_javascript数组扁平化
【JavaScript】实现数组扁平化_程序媛小y的博客-CSDN博客_js数组扁平化处理
-
_new方法
作为构造函数执行的步骤:
扩展一下Object.create
+创建一个空对象
+该对象的__proto__指向你传的参数(它所属构造函数的实例)。合理下传入的应该是一个原型对象proto,那么创建出来的空对象就是proto所属构造函数(该proto的constructor指向)的实例。
这里有一点绕口令:
Fn.prototype属于哪个构造函数的原型?Fn
function Dog(name){
this.name=name;
}
Dog.prototype.bark=function(){
console.log('wang wang')
}
Dog.prototype.sayName=function(){
console.log('my name is' + this.name);
}
let sanmao=new Dog('sanmao');
console.dir(sanmao);
// function _new(Dog,name){
// const obj={};
// obj.name=name;
// obj.__proto__=Dog.prototype;
// return obj;
// }
function _new(Fn,...args){
// let obj={}
// obj.__proto__=Fn.prototype;
let obj=Object.create(Fn.prototype);
Fn.call(obj,...args);
return obj;
}
sanmao=_new(Dog,'liumao')
console.dir(sanmao);
-
数组合并?题意本身不是很明确
数组合并concat
(function(){
let arr1=['A1','A2','B1','B2','C1','C2','D1','D2']
let arr2=['A','B','C','D']
let arr=[]
for(let i=0;i<arr1.length-1;i++){
if(arr1[i][0]<arr1[i+1][0]){
let alpha=arr2.find(item=>item===arr1[i][0])
alpha?arr.push(arr2.shift()):null;
continue;
}
arr.push(arr1[i])
}
if(arr2.length){
// 如果原来数组是乱序的这里还得改一下
arr=arr.concat(arr2);
}
console.log(arr);
console.log(arr1);
console.log(arr2);
})()
function method2(){
let arr1=['A1','A2','B1','B2','C1','C2','D1','D2']
let arr2=['A','B','C','D']
let arr=[]
arr2.forEach((alpha)=>{
let index=arr1.findLastIndex(i=>i[0]===alpha)
arr1.splice(index+1,0,alpha);
})
console.log(arr);
console.log(arr1);
console.log(arr2);
}
method2();
-
闭包
let形成块级作用域
let形成块级作用域。当前i的值存在当前块级作用域里,定时器回调触发的时候找的i是当前块级作用域中的i
大闭包
-
循环体里面整个是一个大函数,保存变量i
for(var i=0;i<10;i++){
(function(i){
// i为参数
setTimeout(()=>{
console.log(i)
},1000)
})(i)
}
把回调处理成闭包*
-
另一种思路是在setTimeout的回调做文章,这里也是一个函数
for(var i=0;i<10;i++){
// setTimeout((function(i){
// return ()=>console.log(i)
// })(i),1000)
// return一个function是在这里做文章
setTimeout(((i)=>()=>console.log(i))(i),1000)
}
function timer(){
return i=>{
setTimeout(()=>console.log(i),1000)
}
}
-
思路一样,使用bind
for(var i=0;i<10;i++){
var fn=function(i){
console.log(i)
}
setTimeout(fn.bind(null,i),1000)
}
-
匿名函数
匿名函数如果设置了函数名
-
外面反而不能用,里面是能用的
-
里面AAA就相当于是一个const变量,
-
你改这个值是没有效果的,但是它不会报错(非strict)
-
但是你前面加了var,本身这个函数名就失效了
设置了名字带来的问题:
外面不能用,里面用了还不能改
当一个变量前没有var let const,沿着上级作用域链查找
-
a==1 & a==2 & a==3
-
toString类型转换
-
两个等号叫比较
三个等号叫绝对比较
(一个等号叫赋值)
==:若数据类型不一样,先转换成一样的再进行比较
===:类型一样值也一样
var a={
i:0,
valueOf:function(){
this.i++;
console.log(this.i);
return this.i;
},
[Symbol.toPrimitive]:function(){
return ++this.i;
}
}
console.log(a==1)
这道题思路:
number类型不可能,bool也不行,字符串……
Array.prototype.toString 以逗号连接数组中的每一项
Object.prototype.toString 用来检测数据类型的
所属类原型上的方法
-
Object.defineProperty
对某个属性的描述,设置某个属性的相关特征
参数:对象 键 描述符
获取这个属性的时候,就会走get函数
// 简单版
let n=0;
Object.defineProperty(window,'a',{
get(){
return ++n;
}
})
if(a==1 && a==2 & a==3){
console.log('OK')
}
Object.defineProperty(window,'a',{
get(){
// 这时的this代表这个属性
this.value?this.value++:this.value=1;
return this.value;
}
})
if(a==1 && a==2 & a==3){
console.log('OK')
}
-
Array.prototype.push原理
-
数组经典排序算法
-
冒泡排序
冒泡:越大的气泡越在上。
当前项和后一项进行比较,最大的排在后面
动图效果:
1662627592813.mp4
引申一下:交换
外层循环控制比较的轮数,n个数我只需要比较n-1次,因为我只需要把n-1个数依次放到末尾;
内层循环控制每轮要比较的次数,
整体思路:
外循环控制多少轮,里层循环控制比较次数,不用跟自己比,做多length-1,然后减掉末尾的数;
-
插入排序
将抓入的牌依次和手里的牌进行比较;
从后往前看,手上的牌小了,就往前挪;大了就插过去
let arr=[9,16,28,18,98,63,3];
for(let i=1;i<arr.length;i++){
// arr[i]:要插入的元素
let insert=arr[i]
let j=i-1;
for(;j>=0;j--){
// 插入的数大于(等于)比较的元素 j+1
if(insert>=arr[j]){
// j+1 -> i-1 往后挪
for(let p=i-1;p>=j+1;p--){
arr[p+1]=arr[p];
}
arr[j+1]=insert;
break;
}
}
// 出循环没插入
if(j<0){
// 0 -> i-1 往后挪
for(let p=i-1;p>=0;p--){
arr[p+1]=arr[p];
}
arr[0]=insert;
}
}
console.log(arr);
let arr=[9,16,28,18,98,63,3];
for(let i=1;i<arr.length;i++){
// arr[i]:要插入的元素
let insert=arr[i]
let j=i-1;
for(;j>=0;j--){
// 插入的数大于(等于)比较的元素 j+1
if(insert>=arr[j]){
// 用splice一定要先删除再插入
// 这里删除的位置一定大于插入的位置,所以不会产生数组塌陷
arr.splice(i,1);
arr.splice(j+1,0,insert)
break;
}
}
// 出循环没插入
if(j<0){
// 用splice一定要先删除再插入
arr.splice(i,1);
arr.splice(0,0,insert)
}
}
console.log(arr);
或者就像视频中新开一个数组,视频中的方法是新开一个数组
https://www.jianshu.com/p/bd0ed19bc158
js 实现排序算法 -- 插入排序(Insertion Sort)
网上很多是这样:
let arr=[9,16,28,18,98,63,3];
for(let i=1;i<arr.length;i++){
// arr[i]:要插入的元素
let insert=arr[i]
let j=i-1;
while(j>=0 && insert<arr[j]){
arr[j+1]=arr[j];
j--;
}
arr[j+1]=insert;
}
console.log(arr);
自己总结:
插入排序其实就是抓牌,将新的牌与手中已有的牌进行比较,从后往前比,当新的牌大于(等于)当前比较的元素时,这个时候插入进去。
所以代码上是:
首先取0号位置作为已排序数组,所以外层循环从1号位置开始循环,i=1;i<length;i++;
从后往前扫描,j=i-1;并且用一个变量temp保存这张新牌即arr[i],从后往前扫描的同时元素依次往后挪,边比较边往后挪,内层循环的条件是 while j>=0 && temp<arr[j],当出循环的时候,j+1号位置就是我们要插入的位置。
-
快速排序
思路:
let arr=[18,24,56,90,3,7,65]
function quick(arr){
let i=Math.floor(arr.length/2);
let middle=arr[i];
arr.splice(i,0);
let left=[],right=[];
arr.forEach(item=>{
if(item<middle) left.push(item)
if(item>=middle) right.push(item)
})
if(left.length>=1) left=quick(left);
if(right.length>=1) right=quick(right)
return left.concat(middle).concat(right)
}
console.log(quick(arr));
快速排序(Quicksort)的Javascript实现 - 阮一峰的网络日志
diff算法 阮一峰_JavaScript实现十大排序算法_短腿胖胖小笨猪的博客-CSDN博客
自己总结:
首先在数组中选一个元素作为基准点,比如一般选中间值,比这个值小的都放到左边,比这个值大的都放到右边,然后分别对左边和右边依次递归,最后返回的是左中右的拼接。
-
-
递归
-
一门语言:
语法本身,设计模式,底层原理,框架,服务器部署,数据库;
这3个排序是入门级别的,二叉树&三叉树,
-
数组填充
let obj={
1:22,
2:333,
12:878,
}
let arr=new Array(12).fill(null);
arr.forEach((i,index)=>{
obj[index+1]?arr[index]=obj[index+1]:null;
})
// 这里用map会更好
arr=new Array(12).fill(null).map((_,index)=>{
return obj[index+1] || null;
})
console.log(arr);
map改变之前的值
let obj={
1:22,
2:333,
12:878,
}
obj.length=13;
arr=Array.from(obj).slice(1).map(i => i || null)
console.log(arr);
-
求交集
注意一下重复元素的情况
let arr1=[1,2,2,76,39,22];
let arr2=[2,39,67,36];
let arr=arr1.reduce((total,item)=>{
let index=arr2.indexOf(item)
if(index>-1){
arr2.splice(index,1)
total.push(item);
}
return total;
},[])
console.log(arr);
思考题:交差并补
-
旋转数组(其实就是将数组平移k个位置)
k写几就是把最后几位放前面
方法一:slice支持负数作为索引
方法二:splice返回删除的数组
方法三:pop unshift
数组切割再拼接
let arr=[1,2,3,4,5,6,7,8]
function rotate(arr,k){
let length=arr.length;
k=k%length;
arr=arr.map((value,index)=>{
return {
value,
index:(index+k)%length
}
})
arr.sort((a,b)=>{
return a.index-b.index;
})
arr=arr.map(i=>(i.value))
return arr;
}
arr=rotate(arr,3);
console.log(arr);
-
函数柯理化
比较简单的做法,这样也行
function add(n1,n2,n3){
return n1+n2+n3;
}
function currying(...args){
if(args.length>=3){
return add(...args)
}
return (...arg)=>{
return currying(...args,...arg);
}
}
console.log(currying(1,2)(3))
console.log(currying(1)(2,3))
console.log(currying(1)(2)(3))
console.log(currying(1,2,3))