八股文(二)

news2024/11/26 18:26:35

一、 实现深拷贝和浅拷贝

1.深拷贝

function checkType(any) {
  return Object.prototype.toString.call(any).slice(8, -1)
}
  //判断拷贝的要进行深拷贝的是数组还是对象,是数组的话进行数组拷贝,对象的话进行对象拷贝
//如果获得的数据是可遍历的,就拿到对应的value,实现深拷贝
function clone(any){
  if(checkType(any) === 'Object') { // 拷贝对象
    let o = {};
    //遍历
    for(let key in any) {
      o[key] = clone(any[key])
    }
    return o;
  } else if(checkType(any) === 'Array') { // 拷贝数组
    var arr = []
    //遍历
    for(let i = 0,leng = any.length;i<leng;i++) {
      arr[i] = clone(any[i])
    }
    return arr;
  } else if(checkType(any) === 'Function') { // 拷贝函数
    return new Function('return '+any.toString()).call(this)
  } else if(checkType(any) === 'Date') { // 拷贝日期
    return new Date(any.valueOf())
  } else if(checkType(any) === 'RegExp') { // 拷贝正则
    return new RegExp(any)
  } else if(checkType(any) === 'Map') { // 拷贝Map 集合
    let m = new Map()
    any.forEach((v,k)=>{
      m.set(k, clone(v))
    })
    return m
  } else if(checkType(any) === 'Set') { // 拷贝Set 集合
    let s = new Set()
    for(let val of any.values()) {
      s.add(clone(val))
    }
    return s
  }
  return any;
}

2.浅拷贝

function shallowCopy(src) {
  var dst = {};
  for (var prop in src) {
  //只要还没读完就继续读,读到的话就直接把属性名返回就行,没有进入深层
  //hasOwnProperty:是用来判断一个对象是否有你给出的名称的属性或对象。有则返回true,没有返回false,不过需要注意的是,此方法无法检查该对象的原型链中是否具有该属性,该属性必须是对象本身的一个成员。
    if (src.hasOwnProperty(prop)) {
      dst[prop] = src[prop];
    }
  }
  return dst;
}

二、 讲一下event loop

事件循环是js引擎执行js的机制,用来实现js的异步特性。事件循环的过程为:当执行栈空的时候,就会从任务队列中,取任务来执行。共分3步:

  • 取一个宏任务来执行。执行完毕后,下一步。

  • 取一个微任务来执行,执行完毕后,再取一个微任务来执行。直到微任务队列为空,执行下一步。

  • 更新UI渲染。

1.EventLoop概念

Event Loop即事件循环,是指浏览器或Node的一种解决javaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。

因为JavaScript就是单线程,也就是说,同一个时间只能做一件事。单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。

所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)

2.同步任务和异步任务

同步任务:函数返回—>拿到预期结果 异步任务:函数返回—>拿不到预期结果,还要通过一定的手段获得结果 简单点说 : 同步任务:会立即执行的任务 异步任务:不会立即执行的任务(异步任务又分为宏任务与微任务)

在异步任务中,任务被分为两种,一种宏任务(MacroTask)也叫Task,一种叫微任务(MicroTask)。

宏任务:由宿主对象发起的任务(setTimeout)。宏任务(定时器)包括 script, setTimeout,setInterval,setImmediate(Node.js),I/O,postMessage, MessageChannel,UI rendering(即I/O、定时器、事件绑定、ajax)

微任务:由js引擎发起的任务(promise)。微任务包括 process.nextTick(Node.js),promise.then(),promise.catch(),MutationObserver。(即Promise的then、catch、finally和process的nextTick)

注意:Promise的then等方法是微任务,而Promise中的代码是同步任务,并且,process的nextTick的执行顺序优先于Promise的then等方法,因为process.nextTick是直接告诉浏览器说要尽快执行,而不是放入队列

3.同步任务

当有多个同步任务时,这些同步任务会依次入栈出栈,如下图

同步任务1先入栈,执行完之后,出栈,接着同步任务2入栈,依此类推

4.异步任务

异步任务会在同步任务执行之后再去执行,那么如果异步任务代码在同步任务代码之前呢?在js机制里,存在一个队列,叫做任务队列,专门用来存放异步任务。也就是说,当异步任务出现的时候,会先将异步任务存放在任务队列中,当执行完所有的同步任务之后,再去调用任务队列中的异步任务

例如下图,现在存在两个同步任务,两个异步任务

js会先将同步任务1提至主线程,然后发现异步任务1和2,则将异步任务1和2依次放入任务队列

然后同步任务1执行完之后出栈,又将同步任务2入栈执行

当同步任务2执行完之后,再从任务队列中提取异步任务执行

 

5.总结

js中,微任务总是先于宏任务执行,也就是说,这三种任务的执行顺序是:同步任务>微任务>宏任务

6.案例

<script>
    console.log(1);
    setTimeout(function(){
        console.log(2)
    },0)
    new Promise((resolve,reject)=>{
        console.log(3)
        resolve()
        console.log(4)
    }).then(()=>{
        console.log(5)
    })
    console.log(6)
</script>

 

三、 js如何判断一个变量是数组

判断变量的类型是最经常使用的方法,但是判断的方式有很多

1.typeof

var ary = [1,2,3,4];
console.log(typeof ary);  // 输出‘object’

很明显,typeof只能检测基本数据类型,并不能检测出来是否为数组。

2.instanceof

var ary = [1,2,3,4];
console.log(ary instanceof Array); //输出true

看起来挺好使的,实际上也确实挺好使的,但是要求变量必须在当前页面声明

3.原型链

var ary = [1,2,3,4];
console.log(ary.__proto__.constructor==Array);  //输出true
console.log(ary.constructor==Array)  //输出true 

两种方法都一样,但是在IE中“proto”没有定义。这种方法也存在和“instanceof”一样的问题,必须先声明变量

4.isArray

let a = [2,543,32];
​
console.log(Array.isArray(a));//true

必须先声明变量

5.完美方法

var arr = [1,2,3,4];
console.log(Object.prototype.toString.call(arr))  //输出"[object Array]"

为了使用简便,我们可以封装成一个函数。

var ary = [1,2,3,4];
function isArray(o){
return Object.prototype.toString.call(o)=='[object Array]';
}
console.log(isArray(ary));

当然,这种方法不仅仅可以用来判断是否为数组,其他的也同样适用。

console.log(Object.prototype.toString.call(123)) //[object Number]
console.log(Object.prototype.toString.call('123')) //[object String]
console.log(Object.prototype.toString.call(undefined)) //[object Undefined]
console.log(Object.prototype.toString.call(true)) //[object Boolean]
console.log(Object.prototype.toString.call({})) //[object Object]
console.log(Object.prototype.toString.call([])) //[object Array]
console.log(Object.prototype.toString.call(function(){})) //[object Function]

四、 JavaScript数据类型

JavaScript共有八种数据类型,分别是 Undefined、Null、Boolean、Number、String、Object、Symbol、BigInt

其中 Symbol 和 BigInt 是ES6 中新增的数据类型:

Symbol 代表创建后独一无二且不可变的数据类型,它主要是为了解决可能出现的全局变量冲突的问题。 BigInt 是一种数字类型的数据,它可以表示任意精度格式的整数,使用 BigInt 可以安全地存储和操作大整数,即使这个数已经超出了 Number 能够表示的安全整数范围。 这些数据可以分为原始数据类型和引用数据类型:

栈:原始数据类型(Undefined、Null、Boolean、Number、String) 堆:引用数据类型(对象、数组和函数)

五、 柯里化

1. 什么是函数柯里化

函数柯里化就是我们给一个函数传入一部分参数,此时就会返回一个函数来接收剩余的参数。 柯里化是一种函数的转换。是指将一个函数从可调用的 f(a, b, c) 转换为 f(a)(b)(c)

2. 简单的柯里化的实现

没有柯里化的实现

将其转化为柯里化的案例

上述代码可简写为

3.函数柯里化的好处

我们希望处理函数时,希望函数功能尽可能单一。如下面代码所示,我们希望第一个参数+2,第二个参数*2,第三个参数** 2,最后再相加,此时我们可以使用函数的柯里化

function makeAdder(count) {
  return function(num) {
    return count + num
  }
}
​
// 在之后使用返回的函数时,我们不需要再继续传入count 了
var adder5 = makeAdder(5)
adder5(10)
adder5(14)
adder5(1100)
adder5(555)

4.柯里化的优点

柯里化是使函数变得更具体些,即作用范围更小

5.传入函数的柯里化执行

function currying(fn, ...rest) {
  return function(...args) {
  //...rest=3,4
  //...args=1,2
    return fn(...rest, ...args);
  }
}
​
function sum(a, b, c, d) {
  console.log(a + b + c + d)
}
//传入sum,即currying(fn,...rest)解析成sum,1,2
//add是接收currying返回的函数
const add = currying(sum, 1, 2);
//add(3,4)即function(3,4),这个function是sum(3,4),所以add(3,4)就会执行最后那个sum四个数相加的函数
add(3, 4);
​
// 执行结果
10
function add(a, b, c, d) {
    console.log(a + b + c + d);
}
​
const curriedAdd = currying(add);
//接收curry()()()()
curriedAdd(1)(2)(3)(4); // 10
curriedAdd(1, 2, 3)(4); // 10
curriedAdd(1, 2, 3, 4); // 10
​
function currying(fn) {
//一开始,curriedAdd传入一个参数1,len=add参数长度4
    const len = fn.length;
//_args=[]
    let _args = [];
    const curry = () => {
//...args=1
        return function (...args) {
//_args.length=0,args.length=1,小于len=4
            // 如果参数攒够了就执行
            if (_args.length + args.length >= len) {
                const result = fn(..._args, ...args);
                // 执行完重置_args
                _args = [];
                return result;
            }
            // 参数不够就继续攒
            else {
 //_args=[..._args,...args]=[1]
                _args = [..._args, ...args];
 //curriedAdd(1)返回值是一个curry函数,以此类推
                return curry();
            }
        }
    }
    return curry();
}

六、 如何理解闭包,为什么有这种特性?为什么需要闭包?闭包的缺点?

(1)什么是闭包?

闭包就是能够读取其他函数内部变量的函数。

由于在javascript中,只有函数内部的子函数才能读取局部变量,所以说,闭包可以简单理解成“定义在一个函数内部的函数“。

所以,在本质上,闭包是将函数内部和函数外部连接起来的桥梁

(2)如何生成闭包?

函数嵌套 + 内部函数被引用。

function outer() {
    var a = 1;
    function inner() {
        console.log(a);
    }
    return inner;
}
//这里的闭包就是返回的inner函数
var b = outer();

(3)闭包作用?

  • 可以让内部的函数访问到外部函数的变量,避免变量在全局作用域中存在被修改的风险

  • 可以读取函数内部的变量

  • 让这些变量的值始终保持在内存中,不会在f1调用后被自动清除

(4)使用闭包的注意事项?

不用的时候解除引用,避免不必要的内存占用。

为什么有闭包的这种特性:如果形成闭包,外部函数执行完后,其中的局部变量可能被内部函数使用,所以不能销毁,因此内部函数能一致访问到这些局部变量,直到引用解除。

(5)闭包的优点?

  • 隐藏变量,避免放在全局有被篡改的风险。

  • 方便调用上下文的局部变量

(6)闭包的缺点?

  • 使用时候不注意的话,容易产生内存泄漏。

  • 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除

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

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

相关文章

小白推荐!必定成功的python的安装流程?

目录 1.安装教程 2.使用cmd测试是否安装成功&#xff0c;快捷键WinR 3.如果测试失败&#xff0c;如何卸载&#xff1f; 4.如何在pycharm中指定下载的python解释器路径&#xff1f; 5.第一条python语句 1.安装教程 1.前往python的官网&#xff08;弄个梯子可能会快一点&#xf…

spring boot + rabbitMq整合之死信队列(DL)

rabbit mq 死信队列 什么是死信队列? DL-Dead Letter 死信队列 死信&#xff0c;在官网中对应的单词为“Dead Letter”&#xff0c;可以看出翻译确实非常的简单粗暴。那么死信是个什么东西呢&#xff1f; “死信”是RabbitMQ中的一种消息机制&#xff0c;当你在消费消息时&…

Qt 解决程序全屏运行弹窗引发任务栏显示

文章目录摘要在VM虚拟机器中测试setWindowFlags()关键字&#xff1a; Qt、 Qt::WindowStayOnTopHint、 setWindowFlags、 Qt::Window、 Qt::Tool摘要 今天眼看项目就要交付了&#xff0c;结果在测试程序的时候&#xff0c;发现在程序全品情况下&#xff0c;点击输入框&#x…

【Android Studio】【学习笔记】【2023春】

文章目录零、常用一、界面布局疑问&报错零、常用 一、界面布局 Android——六大基本布局总结/CSDN小马 同学 【Android】线性布局&#xff08;LinearLayout&#xff09;最全解析/CSDNTeacher.Hu 一个不错的计算器界面&#x1f447; Android Studio App LinearLayout多层…

数据资产管理建设思考(二)

关于数据资产管理&#xff0c;近两年是数据治理行业中一个热点话题&#xff0c;当然有我们前面提到的国家的政策支持及方向指引的原因。另一方面我们做数据治理的同行们从学习吸收国外优秀的数据治理理论&#xff0c;进一步在实践中思考如何应用理论&#xff0c;并结合我们国家…

docker(二)镜像详解、镜像构建、镜像优化

文章目录前言一、docker镜像详解1.镜像分层结构2.镜像的表示二、镜像构建1.commit提交2.DockerfileDockerfile 命令详解三、镜像优化1.缩减镜像层2.多阶段构建3.使用最精简的基础镜像前言 一、docker镜像详解 1.镜像分层结构 共享宿主机的kernelbase镜像提供的是最小的Linux发…

【LeetCode】1487. 保证文件名唯一

1487. 保证文件名唯一 题目描述 给你一个长度为 n 的字符串数组 names 。你将会在文件系统中创建 n 个文件夹&#xff1a;在第 i 分钟&#xff0c;新建名为 names[i] 的文件夹。 由于两个文件 不能 共享相同的文件名&#xff0c;因此如果新建文件夹使用的文件名已经被占用&a…

pytorch-模型训练中过拟合和欠拟合问题。从模型复杂度和数据集大小排查问题

评价了机器学习模型在训练数据集和测试数据集上的表现。如果你改变过实验中的模型结构或者超参数&#xff0c;你也许发现了&#xff1a;当模型在训练数据集上更准确时&#xff0c;它在测试数据集上却不一定更准确。这是为什么呢&#xff1f; 训练误差和泛化误差 在解释上述现象…

常用Swagger注解汇总

常用Swagger注解汇总 前言 在实际编写后端代码的过程中&#xff0c;我们可能经常使用到 swagger 注解&#xff0c;但是会用不代表了解&#xff0c;你知道每个注解都有什么属性吗&#xff1f;你都用过这些属性吗&#xff1f;了解它们的作用吗&#xff1f;本文在此带大家总结一下…

6-2 SpringCloud快速开发入门:声明式服务消费 Feign实现消费者

声明式服务消费 Feign实现消费者 使用 Feign实现消费者&#xff0c;我们通过下面步骤进行&#xff1a; 第一步&#xff1a;创建普通 Spring Boot工程 第二步&#xff1a;添加依赖 <dependencies><!--SpringCloud 集成 eureka 客户端的起步依赖--><dependency>…

图解LeetCode——剑指 Offer 34. 二叉树中和为某一值的路径

一、题目 给你二叉树的根节点 root 和一个整数目标和 targetSum &#xff0c;找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。叶子节点 是指没有子节点的节点。 二、示例 2.1> 示例 1&#xff1a; 【输入】root [5,4,8,11,null,13,4,7,2,null,null,5,1], t…

从 ChatGPT 爆火回溯 NLP 技术

ChatGPT 火遍了全网&#xff0c;多个话题频频登上热搜。见证了自然语言处理&#xff08;NLP&#xff09;技术的重大突破&#xff0c;体验到通用技术的无限魅力。GPT 模型是一种 NLP 模型&#xff0c;使用多层变换器&#xff08;Transformer&#xff09;来预测下一个单词的概率分…

cuda编程以及GPU基本知识

目录CPU与GPU的基本知识CPU特点GPU特点GPU vs. CPU什么样的问题适合GPU&#xff1f;GPU编程CUDA编程并行计算的整体流程CUDA编程术语&#xff1a;硬件CUDA编程术语&#xff1a;内存模型CUDA编程术语&#xff1a;软件线程块&#xff08;Thread Block&#xff09;网格&#xff08…

新界面Moonbeam DApp上线,替你先尝试了一番!

作者&#xff1a;充电中的小恐龙 请注意&#xff0c;本篇内容来自Moonbeam社区成员的无偿分享&#xff0c;与Moonbeam官方和Moonbeam中文社区无关。本文内容仅供参考&#xff0c;对于内容的准确性和实效性&#xff0c;请自行谨慎判断。 本文撰写于DApp上线Beta版本之时&#…

数据库开发(一文概括mysql基本知识)

Mysql 是最流行的关系型数据库管理系统&#xff0c;在 WEB 应用方面 MySQL 是最好的 关系型数据库(Relational Database Management System&#xff1a;关系数据库管理系统)应用软件之一。mysql在问开发中&#xff0c;几乎必不可少&#xff0c;因为其他的可能是要收费的&#x…

【运维】Linux定时任务 定时执行脚本

【运维】Linux定时任务 定时执行脚本 在安装完成操作系统后&#xff0c;默认会安装 crond 服务工具&#xff0c;且 crond 服务默认就是自启动的。crond 进程每分钟会定期检查是否有要执行的任务&#xff0c;如果有&#xff0c;则会自动执行该任务。 五分钟执行一次sh脚本 进入编…

taobao.item.update.delisting( 商品下架 )

&#xffe5;开放平台基础API必须用户授权 单个商品下架输入的num_iid必须属于当前会话用户 公共参数 请求地址: HTTP地址 http://gw.api.taobao.com/router/rest 公共请求参数: 公共响应参数: 点击获取key和secret 请求参数 响应参数 请求示例 TaobaoClient client new …

6-3 SpringCloud快速开发入门: Feign实现负载均衡的服务消费

Feign实现负载均衡的服务消费 负载均衡&#xff1a;Spring Cloud 提供了 Ribbon来实现负载均衡&#xff0c;使用 Ribbo直接注入一个 RestTemplate对象即可&#xff0c;RestTemplate已经做好了负载均衡的配置&#xff1b; 在 Spring Cloud下&#xff0c;使用 Feign也是直接可以实…

实时的软件生成 —— Prompt 编程打通低代码的最后一公里?

PS&#xff1a;这也是一篇畅想&#xff0c;虽然经过了一番试验&#xff0c;依旧有一些不足&#xff0c;但是大体上站得住脚。传统的软件生成方式需要程序员编写大量的代码&#xff0c;然后进行测试、发布等一系列繁琐的流程。而实时生成技术则是借助人工智能技术&#xff0c;让…

在CANoe/CANalyzer中观察CAN Message报文的周期Cycle

案例背景&#xff1a; 该篇博文将告诉您&#xff0c;如何直观的&#xff0c;图示化的&#xff0c;查看CAN网络中各CAN Message报文的周期变化。 优质博文推荐阅读&#xff08;单击下方链接&#xff0c;即可跳转&#xff09;&#xff1a; Vector工具链 CAN Matrix DBC CAN M…