手写 JS 函数,实现数组扁平化 Array Flatten
只减少一级嵌套
<!-- 实现方式1 :使用push-->
function flatten1(arr){
const res=[];
arr.forEach(item=>{
if(Arrery.isArrery(item)){
item.forEach(i=>res.push(i));
}else{
res.push(item);
}
})
return res;
}
<!-- 使用方式2:使用concat -->
function flatten2(arr){
let res=[];
arr.forEach(item=>{
res=res.concat(item);
})
return res;
}
彻底扁平化(需使用递归)
<!-- 实现方式1 :使用push-->
function flattenDeep1(arr){
const res=[];
arr.forEach(item=>{
if(Arrery.isArrery(item)){
const flatItem=flattenDeep1(item);
flatItem.forEach(i=>res.push(i));
}else{
res.push(item);
}
})
return res;
}
<!-- 使用方式2:使用concat -->
function flattenDeep2(arr){
let res=[];
arr.forEach(item=>{
if(Arrery.isArrery(item)){
const flatItem=flattenDeep1(item);
res = res.concat(flatItem);
}else{
res = res.concat(item);
}
})
return res;
}
手写一个 getType 函数,获取详细的数据类型;
function getType(x){
const originType = Object.prototype.toString.call(x);//'[object String]'
const spaceIndex = originType.indexOf(' ');
const type = originType.slice(spaceIndex+1,-1);//String
return type.toLoweCase();//string
}
new 一个对象发生了什么?手写代码表示;
new 一个对象的过程:
创建一个空对象 obj,继承构造函数的原型;
执行构造函数(将 obj 作为 this);
返回 obj;
function customNew(constructor,...args){
//创建一个空对象 obj,继承构造函数的原型;
const obj=Object.create(constructor.prototype);
//执行构造函数(将 obj 作为 this);
constructor.apply(obj,args);
//返回 obj;
return obj;
}
Object.create 和{}有什么区别:
{}创建空对象,原型指向 Object.prototype;
Object.create 创建空对象,原型指向传入的参数;
深度优先遍历一个 DOM 树
//节点访问
function visitNode(n){
if(n instanceof Comment){
//注释
console.info("Comment node----",n.textContent);
}
if(n instanceof Text){
//文本
const t = n.textContent?.trim()
if(t){
console.info("Text node----",t);
}
}
if(n instanceof HTMLElement){
//element
console.info("Element node---",`<${n.tagName.toLowerCase()}>`)
}
}
// 深度优先遍历(递归)
function depthFirstTraverse1(root){
visitNode(root);
const childNodes=root.childNodes;
if(childNodes){
childNodes.forEach(item=>depthFirstTraverse1(item))
}
}
// 深度优先遍历(栈)
function depthFirstTraverse2(root){
const stack=[];
stack.push(root);
while(stack.length>0){
const curNode = stack.pop();
if(curNode==null) break;
visitNode(curNode);
const childNodes=curNode.childNodes;
if(childNodes.length){
Arrery.from(childNodes).reverse().forEach(child=>stack.push(child))
}
}
}
广度优先遍历一个 DOM 树
function breadthFirstTracerse(root){
let queue=[];
queue.unShift(root);
while(queue.length>0){
let curNode = queue.pop();
if(curNode==null) break;
visitNode(curNode);
//子节点入队
const childNodes =curNode.childNode;
if(childNodes.length){
childNodes.forEach(child=>queue.unShift(child))
}
}
}
手写 LazyMan
class LazMan {
private tasks=[];//任务列表
constructor(name){
this.name=name;
setTimeout(()=>this.next());
}
//执行下一个任务
privete next(){
const task=this.tasks.shift();//取出任务列表第一个执行
if(task) task();
}
eat(food){
let tast=()=>{
console.info(`${this.name}吃了${food}`);
this.next();
}
this.tasks.push(tast);
return this;//链式调用
}
sleep(seconds){
let task=()=>{
setTimeout(()=>{
consle.info(`${this.name}睡了${seconds}`);
this.next();
},seconds*1000)
}
this.tasks.push(tast);
return this;//链式调用
}
}
手写 curry 函数,实现函数柯里化
function curry(fn){
const fnArgsLength=fn.length;//获取函数的参数长度
let args=[];
function calc(...newArgs){
args=[ ...args, ...newArgs, ];//累积参数
if(args.length<fnArgsLength){
return calc;//参数不够返回函数;
}else{
return fn.apply(this,args.slice(0,fnArgsLength));
}
}
return calc;
}
function add(a,b,c){
return a+b+c;
}
let curruAdd=curry(add);
res=curruAdd(10)(20)(30);
instanceof 原理是什么,请用代码表示;
function myInstanceof(instance,origin){
if(instance==null) return false;//null,undefined
const type =type instance;
if(type!=='object'&&type!=='function'){
return false;//值类型instanceof返回false
}
let tempInstance=instance;
while(tempInstance){
if(tempInstance.__proto__===origin.prototype){
return true;
}
tempInstance=tempInstance.__proto__;//未匹配上,顺着原型链继续向上找;
}
return false;
}
手写 bind 函数
返回一个新的函数,但是不会执行;
传入的是 this 和参数
参数可以绑定一部分,也可以全部绑定;
Function.prototype.customBind=function(context,...bindArgs){
//context 是bind传入的this;
//bindArgs是bind传入的各个参数;
const self=this;
//返回一个函数
return function (...args){
//拼接参数
const newArgs=bindArgs.contat(args);
return self.apply(this,newArgs)
}
}
手写函数 call 和 apply
bind 返回一个新函数(不执行),call 和 apply 会立即执行函数;
绑定 this;
传入执行参数;
//call
Function.prototype.customCall=function(context,...args){
if(context==null) context=globalThis;//window
if(typeof context !== 'objext') context = new Object(context);//值类型,变换对象类型
const fnKey =Symbol();//不会出现重复属性;
context[fnKey]=this;//this 就是当前函数,把当前函数添加为传入this的属性方法;
const res = context[fnKey](...args) //绑定this,执行函数
delete context[fnKey]; //清理掉fn ,防止污染
return res;
}
//apply 区别为传入的参数为数组
Function.prototype.customApply=function(context,args=[]){
if(context==null) context=globalThis;//window
if(typeof context !== 'objext') context = new Object(context);//值类型,变换对象类型
const fnKey =Symbol();//不会出现重复属性;
context[fnKey]=this;//this 就是当前函数,把当前函数添加为传入this的属性方法;
const res = context[fnKey](...args) //绑定this,执行函数
delete context[fnKey]; //清理掉fn ,防止污染
return res;
}
手写 EventBus 自定义事件
*on once:注册函数,存储起来;(on 绑定的事件可以连续执行,除非 off;once 绑定的函数 emit 一次即删除,也可以未执行而被 off;)
*emit:找到对应的函数存储起来;
*off:找对对应的函数,从对象中删除;
class EventBus {
constructor(){
this.evens={};
/*evens
{
key1:[
{fn:fn1,isOnce:false},
{fn:fn2,isOnce:true},
],
key2:[
{fn:fn1,isOnce:false},
{fn:fn2,isOnce:true},
],
}
*/
}
on(type,fn,isOnce=false){
const events=this.events;
if(event[type]==null) event[type]=[];//初始化 key 的 fn 数组;
event[type].push({ fn,isOnce });
}
once(type,fn){
this.on(type,fn,true)
}
off(type,fn){
if(!fn){
//解绑所有 type 的函数;
this.events[type]=[];
}else{
const fnList = this.events[type];
if(fnList){
this.events[type]=fnList.filter(item=>item.fn=!fn)
}
}
}
emit(type,...args){
const fnList = this.events[type];
this.events[type]=fnList.filter(item=>{
const {fn,isOnce}=item;
fn(...args);
if(!isOnce) return true;
return false;
})
}
}
用 JS 实现一个 LRU 缓存
LRU 缓存:Least Recentiy Used 最近使用,如果内存优先,只缓存最近使用的,删除"沉水"数据,核心 API : get 和 set;
//使用 Map 实现:数据结构为哈希表,并且有序,可排列,则使用Map实现;
class LRUCache{
private length=0;
private data=new Map();
constructor(length){
if(length<1) throw new Error('invalid length');
this.length=length;
}
set(key,value){
if(this.data.has(key)){
this.data.delete(key)
}
this.data.set(key,value);
if(data.size>this.length){
const delKey=this.data.keys().next().value;//获取最老的数据
this.data.delete(delKey);
}
}
get(key){
if(!this.data.has(key)) return null;
//如果数据存在,先删除原有的,再更新成最新的数据
const value=this.data.get(value);
this.data.delete(key);
this.data.set(key,value);
return value;
}
}
手写 JS 深拷贝,考虑各种数据类型和循环引用;
//只考虑简单的数组和对象;
function cloneDeep(obj){
if(typeof obj !=='object' || obj ==null) return obj;
let result;
if(obj instanceof Arrery){
result=[];
}else{
result={};
}
for(let key in obj){
if(obj.hasOwnProperty(key)){
result[key] = cloneDeep(obj[key]) //递归调用
}
}
return result;
}
//考虑各种数据类型和循环引用;
function cloneDeep(obj,map=new weakMap()){
if(typeof obj !=='object' || obj ==null) return obj;
//避免循环引用
const objFromMap = map.get(obj);
if(objFromMap) return objFromMap;
let target = {};
map.set(obj,target);
//map
if(obj instanceof Map){
target = new Map();
obj.forEach((v,k)=>{
const v1 = cloneDeep(v,map);
const k1 = cloneDeep(k,map);
target.set(k1,v1)
})
}
//set
if(obj instanceof Set){
target = new Set();
obj.forEach(v=>{
const v1 = cloneDeep(v,map);
target.add(v1);
})
}
//array
if(obj instanceof Array){
target = obj.map(item=>cloneDeep(item,map))
}
//objec
for(const key in obj){
const val = obj[key];
const val1 = cloneDeep(val,map);
target [key] = val1;
}
return target;
}