js手撕大全
一、数组扁平化⭐⭐⭐⭐⭐
比如一数组:let arr1=[[[[[[1],2],3],4],5],6]
方法一:flat
arr1.flat(Infinity)
方法二:递归
function flat1(arr){
let newArr =[];
arr.forEach(i=>{
if(Array.isArray(i))
{newArr=newArr.concat(flat2(i))}
else{
newArr.push(i)
}
})
return newArr
}
方法三:遍历
function flat1(arr){
while(arr.some(i => Array.isArray(i))){
arr=[].concat(...arr)
}
return arr
}
二、对象扁平化⭐⭐⭐
- prefix:定位递归深度
- res:把扁平化的对象放入
- hasOwnProperty:只处理 obj 自有属性,防止遍历到原型链上其他的键
- value和newkey:获取值和当前完整的键
- 数组判断:引入[]
- 对象判断:递归
function flatObj(obj,prefix='',res={}){
for(let key in obj){
if(Object.prototype.hasOwnProperty.call(obj, key)){
const value = obj[key];
const newkey = prefix?`${prefix}.${key}`:key;
if(Array.isArray(value)){
value.forEach((item,index)=>{
if(typeof item === 'object' && item !==null){
flatObj(item,`${newkey}[${index}]`,res)
}else{
res[`${newkey}[${index}]`]=item
}
})
}else if(typeof value === 'object'&&value!==null){
flatObj(value,newkey,res);
}else{
res[newkey]=value;
}
}
}
return res;
}
三、数组去重⭐⭐⭐⭐⭐
let arr = [1, 2, 3, 2, 4, 1, 5];
方法一:set
result = [...new Set(arr)]
方法二:使用 filter() + indexOf()
- filter用来过滤
- indexOf会得到第一个的索引,
- 通过判断当前索引是不是当前值的第一个索引来进行过滤
let result = arr.filter((item,index)=>arr.indexOf(item) ===index)
方法三:使用reduce()+inclueds()
- reduce():设置acc=[].然后cur来遍历整个数组
- includes():如果没有重复就push到acc
let result2 = arr.reduce((acc,cur)=>{
if(!acc.includes(cur))
{acc.push(cur)}
return acc
},[])
四、对象数组的去重⭐⭐
let users = [ { id: 1, name: "Tom" }, { id: 2, name: "Jerry" }, { id: 1, name: "Tom" }, { id: 3, name: "Spike" } ];
方法一:Map()+reduce()
- reduce():设置acc为{map:new Map(),results:[]},
- 然后每次利用map的has和set方法来判断是否重复
- 如果不重复就push到数组acc.resultws
let results = users.reduce(
(acc,cur) =>{
if(!acc.map.has(cur.id)){
acc.map.set(cur.id,true);
acc.results.push(cur)
}
return acc
}
, {map: new Map(),results:[]}
).results
方法二:Set()+reduce()
- reduce():设置acc为[]。然后对比cur.id存在不存在,不存在就push到acc里面
- Set():Set() 在这里主要用作快速判断重复,与 Map() 的键记录重复的思路类似,但更简洁。
let set1 = new Set()
let results2 = users.reduce((acc,cur)=>{
if(!set1.has(cur.id)){
set1.add(cur.id);
acc.push(cur)
}
return acc
},[])
五、手撕数组的filter()方法⭐⭐
- 传入arguments:callback和thisArg
- 判断callback是不是null,是就throw new TypeError('it is null')
- 判断callback是不是function,不是就throw new TypeError('is is not a function')
- 获取obj:const obj =Object(this)
- 获取正确长度:const len = obj.length >>>0;
- 建立返回的数组:const arr =[]\
- 定义thisArg,通过判定arguments.length是否大于1,来判断是否给thisArg赋值undefiend;
- 遍历obj的索引k,然后通过if(k in obj)去除空洞
- callback(thisArg,obj[k],k,obj)是否为ture
- 判断是否把obj[k]进一步push到arr里面
- 返回arr
Array.prototype.myFilter = function(callback,thisArg){
if(this == null){
throw new TypeError('it is null or undefined')
}
if(typeof callback !== 'function'){
throw new TypeError(callback + 'is not a function');
}
const obj = Object(this)//把this指向arr.myFilter中的arr
const len = obj.length >>>0;//>>>0是为了1.转换为数字2.去掉小数3.去掉负数符号
const arr =[]; //返回的新数组
thisArg = arguments.length>1?arguments[1]:undefined;//这里arguments指定function里面的参数个数
for(let k = 0;k<len;k++){
if(k in obj){//k in obj 是为了跳过空洞
const value = obj[k];
if(callback.call(thisArg,value,k,obj)){
arr.push(value)
}
}
}
return arr;
}
const numbers = [1, 2, 3, 4];
const even = numbers.myFilter(x => x % 2 === 0);
console.log(even); // [2,4]
六、手撕数组的map()方法⭐⭐
- 传入arguments:callback和thisArg
- 判断callback是不是null,是就throw new TypeError('it is null')
- 判断callback是不是function,不是就throw new TypeError('is is not a function')
- 获取obj:const obj =Object(this)
- 获取正确长度:const len = obj.length >>>0;
- 建立返回的数组:const arr =[]\
- 定义thisArg,通过判定arguments.length是否大于1,来判断是否给thisArg赋值undefiend;
- 遍历obj的索引k,然后通过if(k in obj)去除空洞
- callback(thisArg,obj[k],k,obj)的值直接赋值给arr[k]
- 返回arr
Array.prototype.myMap =function (callback,thisArgs){
if(callback == null){ throw new TypeError('it is null or undefined')}
if(typeof callback !=='function') {throw new TypeError('it is not a function')}
const obj = Object(this);
const len = obj.length >>>0;
const arr =[];
thisArgs = arguments.length>1?arguments[1]:null;
for(let i = 0;i<len;i++){
if(i in obj){
arr[i] = callback.call(thisArgs,obj[i],i,obj)
}
}
return arr;
}
let numbers=[1, 2, 3, 4];
console.log(numbers.myMap((numbers)=>{return numbers*2}))
七、深拷贝手撕⭐⭐⭐⭐⭐
- arguments传入为obj和map = new WeakMap
- 判断是不是基本类型或者null
- 判断是不是日期
- 判断是不是正则
- 判断是不是数组
- 判断是不是重复
- 存入哈希map
- 遍历obj
- 判断是不是私有属性
- 执行回调
- 返回
function deep(obj,map = new WeakMap){
if(typeof obj !=='object' || obj ===null) return obj;
if(obj instanceof Date) return new Date(obj);
if(obj instanceof RegExp) return new RegExp(obj);
if (map.has(obj)) return map.get(obj);
let newObj = Array.isArray(obj)?[]:{};
map.set(obj,newObj);
for (let key in obj){
if(Object.prototype.hasOwnProperty.call(obj,key)){
newObj[key]=deep(obj[key],map);
}
}
return newObj;
}
八、手撕节流和防抖⭐⭐⭐⭐⭐
防抖:
- 搜索框输入建议(等用户停止输入后才发送请求)
- 表单验证(用户输入完毕后再验证)
- 窗口调整大小时的重新计算
- 避免按钮重复点击提交
- 即时保存文章草稿
function debounce(func,wait){
let timeout;
return function(...args){
clearTimeout(timeout);
timeout = setTimeout(()=> func.apply(this,args),wait);
}
}
节流:
- 滚动事件处理(如滚动加载、固定导航栏)
- 监听resize事件调整布局
- 拖拽操作更新界面
- 频繁的数据请求或API调用
- 实时游戏中的按键响应
- Canvas 绘图操作
function throttle(func,wait){
let last = 0;
return function(...args){
const now = new Date.now();
if(now-last>wait){
func.apply(this,args);
last = now;
}
}
}
九、寄生组合式继承⭐⭐⭐⭐
- 用this.形式定义实例属性
- 用prototype来定义实例方法
- 用call或者apply来继承父类的实例属性
- 用son.prototype = Object,create(father.prototype)继承方法
- 在用son.prototype.constructor = son修正构造函数指向
//a.父类构造函数
function Father(name){
this.name = name;//实例属性,接受参数name
this.friends = ['jone','tom'];//实例属性,默认属性
};
//原型方法:所有的爸爸father共享的方法
Father.prototype.hello = function(){
console.log(`hello,i am ${this.name}`)//当前实例的name
};
//b.子类构造函数
function Son(name,age){
Father.call(this,name);//调用父构造函数,继承实例属性
this.age = age;//子类构造函数自己的实例
};
//c.原型链继承
Son.prototype = Object.create(Father.prototype);//继承父类源方法
Son.prototype.constructor = Son;//修正构造函数指向
//d.验证
let son = new Son('jojo',22);
son.hello();
console.log(son);
console.log(son.age);
console.log(son.friends);
十、持续更新中
#第一次找实习,我建议__#前端面试笔记 文章被收录于专栏
双非本前端求职笔记,八股文项
查看13道真题和解析