面试题手撕篇

news2025/1/10 20:39:37

参考博客

开始之前,理解递归

在这里插入图片描述

手写 浅拷贝

function shallow(target){
    if(target instanceof Array){
        return [...resObj]
    }else{
        return Object.assign({},target);
    }
}

手写深拷贝

const _sampleDeepClone = target => {
                // 补全代码
                return JSON.parse(JSON.stringify(target))
            }

无法处理处理循环引用的问题

完整写法

function DeepCopy(target, map = new Map()) {
  //对于普通类型 直接返回即可
  if(typeof target!=='object' || target===null){return target}
  //主要处理的是引用类型

  let result = Array.isArray(target)?[]:{};

  if (map.has(target)) {
    result = map.get(target);
  } else {
    map.set(target, result);
    for (let key in target) {
      if (target.hasOwnProperty(key)) {
        if (typeof target[key] === "object") {
          result[key] = DeepCopy(target[key], map);
        } else {
          result[key] = target[key];
        }
      }
    }
  }
  return result;
}

手写new

当你使用 new Person() 来调用构造函数 Person 时,它会创建一个新的对象,并将该对象的原型设置为 Person.prototype。这个新对象可以通过 this 关键字在构造函数内部访问和设置属性。

在这里插入图片描述
在这里插入图片描述

function Person(name, age){
  this.name=name;
  this.age=age;
  return{
    abc:'abc'
  } 
  //mynew中最后的判断就是为了防止这种情况:构造函数会返回值,而不会创建新的实例
}

function myNew(func, ...args) {
  //接受不确定长度的参数放到args数组中

  if (typeof func !== "function") {
    return new TypeError("fn must be a function");
  }

  //把新对象的__proto__指向func.prototype
  let obj = Object.create(func.prototype);
  //构造函数内部的 this 值指向新创建的对象,从而在构造函数中正确地初始化新对象的属性和方法。
  //apply会立即执行
  //result接受执行结果
  let result = func.apply(obj, args);
  //如果构造函数有返回值,则返回这个返回值,没有的话返回新创建的对象
  if (result && (typeof result === "object" || typeof result === "function")) {
    return result;
  } else {
    return obj;
  }
}

// const f1=new Person("xiaoming",22)
const f2= myNew(Person,"xiaoming",22)
console.log(f2);

手写instanceof

在这里插入图片描述

function myInstanceof(obj,target){
//obj是被判断的对象
//target是一个构造函数
  if(typeof obj!=='object'|| typeof target !== 'function'){
    return new TypeError("inputs must be object type!")
  }


  let objProto = obj.__proto__
  while(objProto!==null){
  //注意这里用的是 构造函数.prototype
    if(objProto===target.prototype) return true
    objProto = obj.__proto__
  }

  return false
}
console.log(myInstanceof([1,2],Array));

Object.create

Object.create(proto)返回一个新对象,新对象的原型是proto

【可以不看】proto应该填入一个对象,而不是一个构造函数。
如果你使用 Object.create(Person),而不是 Object.create(Person.prototype)
person 对象的原型将直接设置为 Person 构造函数本身,而不是 Person 构造函数的原型。这意味着 person 对象将无法继承 Person.prototype 上的属性和方法。
这意味着 person 对象将继承 Function.prototype 上的属性和方法,而不是 Person.prototype 上的属性和方法。

在这里插入图片描述


function myOBJcreate(proto){
  function f(){}
  f.prototype = proto

  return new f()
}

const obj1={
  name:'alice',
  age:11
}
const f1 = Object.create(obj1)
const f2 = myOBJcreate(obj1)
//不需要比较,因为f1 f2是两个不同的对象

手写 防抖

主要思路:定时器控制1s后触发,并且保证只有一个定时器会被触发(timer的作用)
其实就是用定时器,来控制,这个函数只能1s后触发。

    let Inp = document.querySelector("input");
    function getValue() {
        console.log(`获取${Inp.value}`);
    }
    Inp.addEventListener("keyup", debounce(getValue, 1000));

//业务代码
    function debounce(func, time) {
        let timer = null;
        return function () {
            if (!timer) {
                timer = setTimeout(() => {
                    func.apply(this, arguments);
                    timer = null;
                }, time);
            }
        };
    }

如果你想传参:

    let Inp = document.querySelector("input");
    function getValue(name) {
      console.log(`${name}获取${Inp.value}`);
    }
    newfunc = debounce(getValue, 1000);
    Inp.addEventListener("keyup", ()=>{
        newfunc("xiaoming")
    });

    function debounce(func, time) {
      let timer = null;
      return function () {
        if (!timer) {
          timer = setTimeout(() => {
            func.apply(this, arguments);
            timer = null;
          }, time);
        }
      };
    }

节流防抖巧记!

下面是业务代码,只有一行不同。
可以先把防抖的写出来(防抖的更容易理解),然后吧func.apply移出timer即可。
即节流第一次触发就要执行。

    function debounce(func, delay) {
      let timer = null;
      return function () {
        if (!timer) {
          timer = setTimeout(() => {
            func.apply(this, arguments);
            timer = null;
          }, delay);
        }
      };
    }

    const throttle = (func, delay) => {
      let timer;
      return function () {
        if (!timer) {
          func.apply(this, arguments);
          timer = setTimeout(() => {
            timer = null;
          }, delay);
        }
      };
    };

手写 节流(另一种方式,了解)

主要思路:判断上次触发的时间和这次的时间
使用方法是这样的:

const throttledFunction = throttle(myFunction, 1000); // 设置节流时间为1秒

throttledFunction就是一个具有节流功能的函数。你可以调用它来代替原始的myFunction函数。

const throttle = (func, delay) => {
  let timer;
  return function () {
    if (!timer) {
      func.apply(this, arguments);
      timer = setTimeout(() => {
        timer = null;
      }, delay);
    }
  };
};

function test(name){
  this.name = name;
  console.log(`name is ${name}`);
}

let newfunc = throttle(test,5000)

newfunc('xiaoming') //只会运行1个
newfunc('xiaoming')

手写call

函数科里化的实现

前置:reduce方法:arr.reduce((accumulator, currentValue, currentIndex, array)=>{},初始值)

实现add函数:
在这里插入图片描述

function add(...args){
  const sum = args.reduce((total,num)=>total+num,0);

  //用curry判断还有没有剩余参数
  function curry(...nextArgs){
    //闭包,递归的终止条件
    //如果没有剩余参数,输出结果
    if(nextArgs.length===0){return sum;}
    //如果有,则继续加
    return add(sum,...nextArgs);
  }

  return curry;
}

console.log(add(1)(2)(3)()); // 输出: 6
console.log(add(1, 2, 3)(4)()); // 输出: 10
console.log(add(1)(2)(3)(4)(5)()); // 输出: 15

promise.all

要点:

  • promise里面不需要return,而是需要resolve和reject
  • 如果在Promise.all中的Promise数组中有一个或多个Promise被拒绝(即失败),Promise.all返回的新的Promise会立即被拒绝,并且会传递第一个被拒绝的Promise的错误原因。

思路:for循环遍历所有的promises数组,所有的都成功则resolve,有一个失败则立即reject

function myPromiseAll(promises) {
  return new Promise((resolve, reject) => {
    if (!Array.isArray(promises)) {
      reject(new TypeError("参数必须是一个数组"));
    }
    let results = [];
    if (promises.length === 0) {
      resolve(results);
    }
    for (let promise of promises) {
      promise
        .then((res) => {
          results[results.length] = res;
          if (results.length === promises.length) {
            return resolve(results);
          }
        })
        .catch((error) => {
          reject(error);
        });
    }
  });
}

Promise.race

要点:
如果在 Promise.race 中的第一个 Promise 对象被拒绝(rejected),则整个 Promise.race 会立即拒绝(reject)并返回该拒绝的原因。后续的 Promise 对象不会再被执行。

function myPromiseRace(promises) {
  return new Promise((resolve, reject) => {
    if (!Array.isArray(promises)) {
      reject(new TypeError("argus must be a array"));
    }

    for (let promise of promises) {
      promise
        .then((result) => {
          resolve(result);
        })
        .catch((error) => {
          reject(error);
        });
    }
  });
}

数组扁平化

方法1:展开数组,层层剥开【推荐】

let arr = [1, [2, [3, 4, 5]]]

function flatten2(arr){
  while(arr.some((item)=>Array.isArray(item))){
    arr = [].concat(...arr)
  }
  return arr;
}

console.log(flatten2(arr));

方法2:判断当前项是否为数组 如果是数组递归调用 不是就push到新数组

function flatten(arr) {
  let newArr=[];

  for(let item of arr){
    if(Array.isArray(item)){
      newArr = newArr.concat(flatten(item))
    }else{
      newArr.push(item)
    }
  }
  return newArr;
}

数组去重

两种方法

function only1(arr){
  return arr.filter((value,index)=>{return arr.indexOf(value)===index})
}

其中,indexOf 方法会返回指定元素在数组中第一次出现的索引

function only(arr){
  return [...new Set(arr)]
}

实现reduce

array.reduce(callbackfn: (previousValue,currentValue, currentIndex, array), initialValue )

Array.prototype.myReduce = function(callback, initialValue){
  //判断参数是否正确
  if(typeof callback !=='function'){return new TypeError("callback must be a function")}
  let accumulator;

  //检查有没有设置初始值
  if(!initialValue){
    if(this.length===0){return new TypeError("can't reduce a empty array")}
    accumulator = this[0]
  }else{
    accumulator = initialValue
  }

  //循环调用callback
  for(let i = initialValue?0:1;i<this.length;i++){
    accumulator = callback(accumulator,this[i],i,this)
  }
  //返回结果值
  return accumulator;
}
console.log([1,2,3].myReduce((pre,cur)=>{return pre+cur},0));

实现push

Array.prototype.myPush = function(){
  for(let i=0;i<arguments.length;i++){
    this[this.length]=arguments[i]
  }
  return this.length;
}

数组转树

测试数据:

let source = [
  {
    id: 1,
    pid: 0,
    name: 'body',
  },
  {
    id: 2,
    pid: 1,
    name: 'title',
  },
  {
    id: 3,
    pid: 2,
    name: 'div',
  },
  {
    id: 4,
    pid: 0,
    name: 'html',
  },
  {
    id: 5,
    pid: 4,
    name: 'div',
  },
  {
    id: 6,
    pid: 5,
    name: 'span',
  },
  {
    id: 7,
    pid: 5,
    name: 'img',
  },
][
  // 转为
  ({
    id: 1,
    pid: 0,
    name: 'body',
    children: [
      {
        id: 2,
        pid: 1,
        name: 'title',
        children: [{ id: 3, pid: 2, name: 'div' }],
      },
    ],
  },
  {
    id: 4,
    pid: 0,
    name: 'html',
    children: [
      {
        id: 5,
        pid: 4,
        name: 'div',
        children: [{ id: 7, pid: 5, name: 'img' }],
      },
    ],
  })
]

普通版本:

    function buildTree(arr, parentId) {
      let result = [];
      for (const item of arr) {
        if (item.pid === parentId) {
          item.children = buildTree(arr, item.id);
          result.push(item);
        }
      }
      return result;
    }

可以防止循环引用的版本:

    function buildTree(arr, pid = 0) {
      const nodeMap = new Map();
      const result = [];

      arr.forEach((item) => nodeMap.set(item.id, { ...item, children: [] }));

      for (const item of arr) {
        if (item.pid === pid) {
          const children = buildTree(arr, item.id);
          nodeMap.get(item.id).children = children;
          result.push(nodeMap.get(item.id));
        }
      }

      return result;
    }

树转数组

function TreeToArray(arr){
    let result = []
    for(let item of arr){
        if(item.children){
            result = result.concat(TreeToArray(item.children))
            delete item.children
            result.push(item)
        }else{
            result.push(item)
        }              
    }
    return result;
}

斐波那契数列的迭代和递归实现

    function fiber(n) {
      if (n >= 0) {
        if (n == 0) return 0;
        if (n == 1) return 1;
        return fiber(n - 1) + fiber(n - 2);
      }
    }
    function fiber2(n){
        let F=[]
        F[0]=0;
        F[1]=1;
        for(let i=2;i<=n;i++){
            F[i]=F[i-1]+F[i-2]
        }
        console.log(F);
        return F[n]
    }

实现每隔一秒打印 1,2,3,4

注意那个时间一定要写对!!1000*i

    for(let i=1;i<5;i++){
        setTimeout(()=>{
            console.log(i)
        },1000*i)
    }

使用 setTimeout 实现 setInterval

视频bilibili
这个问题的背景:
setInterval有时会失效,比如下面这个例子:
在这里插入图片描述
这样的话,每次调用这个回调之间的间隔就会超过1s,因为后面一个回调需要等待前面一个回调完成。

运行:
在这里插入图片描述
正常情况下:
在这里插入图片描述
不正常情况下:
在这里插入图片描述


//基础版

    function newInterval(fn, delay) {
        function inside(){
            fn()
            setTimeout(inside,delay);
        }
        inside();
    }

上面的代码有个缺点就是第一个 inside();执行的fn不会被放入任务队列,而是放入了执行栈,这与setinerval的行为不符,为了更加模拟,可以这样修改:

function newInterval(func, delay){
	function inside(){
		func()
		setTimeout(inside,delay)
	}
	setTimeout(inside,delay);
}

完整版:加上取消定时器的功能:

function newInterval(func,delay){
let timer;
	function inside(){
		func();
		timer = setTimeout(inside,delay)
	}
	timer = setTimeout(inside,delay)
	
	return{
		cancel:function(){
			clearTimeout(timer);	
		}
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1520353.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度学习神经网络训练环境配置以及演示

&#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;高性能&#xff08;HPC&#xff09;开发基础教程 &#x1f380;CSDN主页 发狂的小花 &#x1f304;人生秘诀&#xff1a;学习的本质就是极致重复! 目录 1 NVIDIA Dr…

Flink源码解析(1)TM启动

首先在看之前,回顾一下akka模型: Flink通讯模型—Akka与Actor模型-CSDN博客 注:ActorRef就是actor的引用,封装好了actor 下面是jm和tm在通讯上的概念图: RpcGateway 用于定义RPC协议,是客户端和服务端沟通的桥梁。服务端实现了RPC协议,即实现了接口中定义的方法,做具…

云原生(二)、Docker基础

Docker Docker 是一种开源的容器化平台&#xff0c;用于开发、部署和运行应用程序。它允许开发者将应用程序及其所有依赖项打包到一个可移植的容器中&#xff0c;这个容器可以在任何支持 Docker 的环境中运行&#xff0c;无论是开发人员的个人笔记本电脑、测试环境、生产服务器…

逆序对的数量 刷题笔记

思路 使用归并排序 在每次返回时 更新增加答案数 因为归并排序的两个特点 第一 使用双指针算法 第二 层层返回 从局部有序合并到整体有序 例如 {4 &#xff0c;1 &#xff0c;2 &#xff0c;3} 划分到底层是四个数组 {4}&#xff0c;{1}&#xff0c;{3}, {…

Java项目:54 springboot工资信息管理系统453

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 本系统的使用角色可以被分为用户和管理员&#xff0c; 用户具有注册、查看信息、留言信息等功能&#xff0c; 管理员具有修改用户信息&#xff0c;发…

k8s部署hadoop

&#xff08;作者&#xff1a;陈玓玏&#xff09; 配置和模板参考helm仓库&#xff1a;https://artifacthub.io/packages/helm/apache-hadoop-helm/hadoop 先通过以下命令生成yaml文件&#xff1a; helm template hadoop pfisterer-hadoop/hadoop > hadoop.yaml用kube…

《操作系统实践-基于Linux应用与内核编程》第10章-Linux综合应用

前言: 内容参考《操作系统实践-基于Linux应用与内核编程》一书的示例代码和教材内容&#xff0c;所做的读书笔记。本文记录再这里按照书中示例做一遍代码编程实践加深对操作系统的理解。 引用: 《操作系统实践-基于Linux应用与内核编程》 作者&#xff1a;房胜、李旭健、黄…

vue3+ts动态表单渲染,antd的useForm改造

let fieldList: any getFormFields(fieldInfo.coreNavigationList[0].list[0].list,fieldInfo.positionCodeRespVO,isCanBeUpdateProcess.value,isDetail.value 1); fieldInfo数据格式&#xff1a; {"name": "默认模板","status": "ENA…

微信小程序-webview分享

项目背景 最近有个讨论区项目需要补充分享功能&#xff0c;希望可以支持在微信小程序进行分享&#xff0c;讨论区是基于react的h5项目&#xff0c;在小程序中是使用we-view进行承载的 可行性 目标是在打开web-view的页面进行分享&#xff0c;那就需要涉及h5和小程序的通讯问…

K8S CNI

OCI概念 OCI&#xff0c;Open Container Initiative&#xff0c;开放容器标准&#xff0c;是一个轻量级&#xff0c;开放的治理结构&#xff08;项目&#xff09;&#xff0c;在 Linux 基金会的支持下成立&#xff0c;致力于围绕容器格式和运行时创建开放的行业标准。 OCI 项目…

虚拟机开机字体变大,进入系统后字体模糊

问题 虚拟机开机字体变大&#xff0c;进入系统后字体模糊。 原因 虚拟机配置问题。 解决办法 修改配置为如下:

【兆易创新GD32H759I-EVAL开发板】图像处理加速器(IPA)的应用

GD32H7系列的IPA&#xff08;Image Pixel Accelerator&#xff09;是一个高效的图像处理硬件加速器&#xff0c;专门设计用于加速图像处理操作&#xff0c;如像素格式转换、图像旋转、缩放等。它的优势在于能够利用硬件加速来实现这些操作&#xff0c;相比于软件实现&#xff0…

Ubuntu软件开发环境搭建

Ubuntu软件开发环境搭建 安装VMware Tools网络桥接更新软件源常用功能配置时间同步共享文件夹双向复制粘贴终端初始大小和字体设置安装必要的工具 常用指令 安装VMware Tools 点击虚拟机->安装VMware Tools… 打开终端&#xff0c;cd到/media/用户名/VMware Tools/下&#…

JS 事件捕获、事件冒泡、事件委托

js事件机制在开发中可以说时刻使用&#xff0c;例如dom绑定事件、监听其自身事件等。js事件机制有事件捕获、事件冒泡俩种机制&#xff0c;我们分别说下这俩种机制的使用场景。 一、概念 事件捕获顺序如下&#xff1a; window > document > body > div 事件冒泡顺序…

【一】【单片机】有关LED的实验

点亮一个LED灯 根据LED模块原理图&#xff0c;我们可以知道&#xff0c;通过控制P20、P21...P27这八个位置的高低电平&#xff0c;可以实现D1~D8八个LED灯的亮灭。VCC接的是高电平&#xff0c;如果P20接的是低电平&#xff0c;那么D1就可以亮。如果P20接的是高电平&#xff0c;…

14.矩阵置零

给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0,1],[0,0,0],[1,0,1]]示例 2&#xff1a; 输入&…

Hello,Spider!入门第一个爬虫程序

在各大编程语言中&#xff0c;初学者要学会编写的第一个简单程序一般就是“Hello, World!”&#xff0c;即通过程序来在屏幕上输出一行“Hello, World!”这样的文字&#xff0c;在Python中&#xff0c;只需一行代码就可以做到。我们把这第一个爬虫就称之为“HelloSpider”&…

3.2_3 页面置换算法

3.2_3 页面置换算法 请求分页存储管理与基本分页存储管理的主要区别&#xff1a; 在程序执行过程中&#xff0c;当所访问的信息不在内存时&#xff0c;由操作系统负责将所需信息从外存调入内存&#xff0c;然后继续执行程序。 若内存空间不够&#xff0c;由操作系统负责将内存中…

Laravel Class ‘Facade\Ignition\IgnitionServiceProvider‘ not found 解决

Laravel Class Facade\Ignition\IgnitionServiceProvider not found 问题解决 问题 在使用laravel 更新本地依赖环境时&#xff0c;出现报错&#xff0c;如下&#xff1a; 解决 这时候需要更新本地的composer&#xff0c;然后在更新本地依赖环境。 命令如下&#xff1a; co…

Day43-2-企业级实时复制intofy介绍及实践

Day43-2-企业级实时复制intofy介绍及实践 1. 企业级备份方案介绍1.1 利用定时方式&#xff0c;实现周期备份重要数据信息。1.2 实时数据备份方案1.3 实时复制环境准备1.4 实时复制软件介绍1.5 实时复制inotify机制介绍1.6 项目部署实施1.6.1 部署环境准备1.6.2 检查Linux系统支…