栈在前端中的应用,顺便再了解下深拷贝和浅拷贝!

详解栈在前端中的应用

  • 一、栈是什么
  • 二、栈的应用场景
  • 三、前端与栈:深拷贝与浅拷贝
    • 1、JS数据类型
      • (1)js数据类型的分类
      • (2)js数据类型的定义和存储方式
      • (3)js数据类型的判断方式
    • 2、深究浅拷贝和深拷贝
      • (1)浅拷贝
      • (2)深拷贝
  • 四、前端与栈:函数调用堆栈
  • 五、写在最后

在日常生活中的应用非常广泛,比如我们最熟悉不过的十进制转二进制、迷宫求解等等问题。同时,它在前端中的应用也非常广泛,很多小伙伴都会误以为 在前端中的应用很少,但殊不知的是,我们写的每一个程序,基本上都会用到 这个数据结构。比如,函数调用堆栈、数据的深拷贝和浅拷贝……。

所以呢,对于一个前端工程师来说, 结构是一个必学的知识点。在接下来的这篇文章中,将讲解关于 在前端中的应用。

一、栈是什么

  • 栈是一种只能在表的一端(栈顶)进行插入和删除运算的线性表
  • 只能在栈顶运算,且访问结点时依照后进先出 (LIFO) 或先进后出 (FILO) 的原则。

二、栈的应用场景

  • 需要后进先出的场景;
  • 比如:十进制转二进制、迷宫求解、马踏棋盘、判断字符串是否有效、函数调用堆栈……。

三、前端与栈:深拷贝与浅拷贝

1、JS数据类型

谈到堆栈,我们需要先来了解一下关于 js 的两种数据类型。

(1)js数据类型的分类

首先,JavaScript中的数据类型分为基本数据类型引用数据类型

了解完分类以后,相信很多小伙伴心里有一个疑惑:这两个数据类型是什么呢?且在内存中是存放在哪里呢?

(2)js数据类型的定义和存储方式

基本数据类型:

基本数据类型,是指 NumerBooleanStringnullundefinedSymbol(ES6新增的)BigInt(ES2020) 等值,它们在内存中都是存储在 中的,即直接访问该变量就可以得到存储在 中的对应该变量的值。

若将一个变量的值赋值给另一个变量,则这两个变量在内存中是独立的,修改其中任意一个变量的值,不会影响另一个变量。这就是基本数据类型。

引用数据类型:

那引用数据类型呢,是指 ObjectArrayFunction 等值,他们在内存中是存在于 栈和堆 当中的,即我们要访问到引用类型的值时,需要先访问到该变量在 中的地址(这个地址指向堆中的值),然后再通过这个地址,访问到存放在 中的数据。这就是引用数据类型。

这样说可能有点抽象,让我们用一张图来理解一下。

基本数据类型和引用数据类型

从上图中可以看到, nameage 的值都是基本数据类型,所以他们指向程序中 的位置。而 like 是数组类型,也就是引用数据类型,所以在 中,它先存放了一个 like 的地址,之后再把 like 对应的值,存放到 当中。

了解完数据类型和其存储方式后,在面试中,还有可能被问到如何判断某一个数据的类型是什么?什么意思呢?比如说,给你一个数字 7 ,需要你来判断它是什么,我们都知道它是Number类型,但很多时候止步于如何做才能判断它是一个Number类型。接下来将详细介绍三种判断数据类型的方法。

(3)js数据类型的判断方式

常用判断方式:typeof、instanceof、===

1)typeof:

定义:返回数据类型的字符串表达(小写)

用法:typeof + 变量

可以判断

  • undefined / 数值 / 字符串 / 布尔值 / function ( 返回 undefined / number / string / boolean / function
  • nullobjectarray (null、array、object都会返回 object

以下给出代码演示:

<script type="text/javascript">console.log(typeof "Tony");                // 返回 string console.log(typeof 5.01);                  // 返回 numberconsole.log(typeof false);                 // 返回 booleanconsole.log(typeof undefined);             // 返回 undefinedconsole.log(typeof null);                  // 返回 objectconsole.log(typeof [1,2,3,4]);             // 返回 objectconsole.log(typeof {name:'John', age:34}); // 返回 object
</script>

2)instanceof:

定义:判断对象的具体类型

用法:b instanceof A → b是否是A的实例对象

可以判断

  • 专门用来判断对象数据的类型: Object , ArrayFunction

  • 判断 StringNumberBoolean 这三种类型的数据时,直接赋值为 false ,调用构造函数创建的数据为 true

以下给出代码演示:

<script type="text/javascript">let str = new String("hello world")     //console.log(str instanceof String);  → truestr = "hello world"                     //console.log(str instanceof String);  → falselet num = new Number(44)                //console.log(num instanceof Number);  → truenum = 44                                //console.log(num instanceof Number);  → falselet bool = new Boolean(true)            //console.log(bool instanceof Boolean);  → truebool = true                             //console.log(bool instanceof Boolean);  → false</script>
<script type="text/javascript">let items = []; let object = {}; function reflect(value) {return value; } console.log(items instanceof Array);        // true console.log(items instanceof Object);       // true console.log(object instanceof Object);      // true console.log(object instanceof Array);       // false console.log(reflect instanceof Function);   // true console.log(reflect instanceof Object);     // true 
</script>

3)===:

可以判断:undefined,null

以下给出代码演示:

<script type="text/javascript">let str;console.log(typeof str, str === undefined);   //'undefined', truelet str2 = null;console.log(typeof str2, str2 === null); // 'object', true</script>

讲到这里,我们了解了js的两种数据类型,以及两种数据类型相关的存储方式和判断方式。那么,接下来将讲解他们在前端中常见的应用,深拷贝和浅拷贝。

2、深究浅拷贝和深拷贝

(1)浅拷贝

1)定义

所谓浅拷贝,就是一个变量赋值给另一个变量,其中一个变量的值改变,则两个变量的值都变了,即对于浅拷贝来说,是数据在拷贝后,新拷贝的对象内部 仍然有一部分数据 会随着源对象的变化而变化。

2)代码演示

// 浅拷贝-分析
function shallowCopy(obj){let copyObj = {};for(let i in obj){copyObj[i] = obj[i];}return copyObj;
}// 浅拷贝-实例
let a = {name: '张三',age: 19,like: ['打篮球', '唱歌', '跳舞']
}//将a拷贝给b
let b = shallowCopy(a);a.name = '李四';
a.like[0] = '打乒乓球';
console.log(a);
/*
*{name: '李四',age: 19,like: ['打乒乓球', '唱歌', '跳舞']}
*/
console.log(b);
/*
*{name: '张三',age: 19,like: ['打乒乓球', '唱歌', '跳舞']}
*/

3)图例

从上面中的代码可以看到,我们明明把 a 对象拷贝给 b 了,但是 b 最终打印出来的结果部分数据不变,部分数据却变了。这个时候很多小伙伴就很疑惑了,这究竟是为什么呢?

我们回顾上面所说到的关于 引用数据类型 的知识点,上述代码中的 b 中的 like ,是一个数组,也就是引用数据类型。我们都知道,引用数据类型的数据是存放于 栈和堆 当中的,所以上述中的 like 数组,我们将它视为一个地址,这个地址存放于 当中,同时,这个地址里面的数据,就指向于 当中。我们来看一下图例。

浅拷贝

从上图中可以看到,当对 alike 的数据进行改变时,它对应的数据在 中改变。而 b 拷贝后的 like 地址所指向的数据,也是跟 a 一样在 中的位置。也就是说,ab 中的 like 地址,它们的数据指向 中的同一个位置,所以 b 在拷贝完数据以后,部分数据会随着 a 的变化而变化。这就是浅拷贝。

讲完浅拷贝,接下来来了解深拷贝。

(2)深拷贝

1)定义:深拷贝就是,新拷贝的对象内部所有数据都是 独立存在 的,不会随着源对象的改变而改变。

2)深拷贝有两种方式:递归拷贝和利用 JSON 函数进行深拷贝。

  • 递归拷贝的实现原理是:对变量中的每个元素进行获取,若遇到基本类型值,直接获取;若遇到引用类型值,则继续对该值内部的每个元素进行获取。
  • JSON深拷贝的实现原理是:将变量的值转为字符串形式,然后再转化为对象赋值给新的变量。

3)局限性:深拷贝的局限性在于,会忽略undefined,不能序列化函数,不能解决循环引用的对象。

4)代码演示

// 深拷贝-递归函数方法分析
function deepCopy(obj){// 判断是否为引用数据类型if(typeof obj === 'object'){let result = obj.constructor === Array ? [] : {};for(let i in obj){result[i] =  typeof obj[i] === 'object' ? deepCopy(obj[i]) : obj[i];}return result;}// 为基本数据类型,直接赋值返回else{return obj;}
}// 深拷贝-递归函数方法实例
let c = {name:'张三',age:12,like:['打篮球','打羽毛球','打太极']
}let d = deepCopy(c);c.name = '李四';
c.like[0] = '打乒乓球';
console.log(c);
/*
*{name: '李四',age: 19,like: ['打乒乓球', '打羽毛球', '打太极']}
*/
console.log(d);
/*
*{name: '张三',age: 19,like: ['打篮球', '打羽毛球', '打太极']}
*/
// 深拷贝-JSON函数方法实例
let c = {name: '张三',age: 19,like:['打篮球', '唱歌', '跳舞']
}let d = JSON.parse(JSON.stringify(c));// 注意: JSON函数做深度拷贝时不能拷贝正则表达式,Date,方法函数等c.name = '李四';
c.like[0] = '打乒乓球';console.log(c);
/*
*{name: '李四',age: 19,like: ['打乒乓球', '唱歌', '跳舞']}
*/
console.log(d);
/*
*{name: '张三',age: 19,like: ['打篮球', '唱歌', '跳舞']}
*/

从上述代码中可以看到,深拷贝后的数据各自都是独立存在的,不会随着源对象的变化而变化,这就是深拷贝。不过值得注意的是,在我们平常的开发中,用的更多的是递归函数来进行深拷贝,原因在于递归函数方法的灵活性会更强一点。而 JSON 函数方法有很多局限性,在做深度拷贝时不能拷贝正则表达式、Date、方法函数等。

四、前端与栈:函数调用堆栈

在我们平常的开发中,经常会写很多函数,那函数在执行过程中,其实就是一个调用堆栈。接下来我们用一段代码来演示。

const func1 = () => {func2();console.log(3);
}const func2 = () => {func3();console.log(4);
}const func3 = () => {console.log(5);
}func1(); //5 4 3

看到这里,很多小伙伴心中可能已经在构思整段代码的执行顺序是什么样的。接下来用一张图来展示。

函数调用堆栈

我们都知道, JavaScript 的执行环境是单线程的。所谓单线程是指一次只能完成一个任务,如果有多个任务,就必须排队,只有当前面一个任务完成时,才能执行后面一个任务,以此类推。上图中所演示的,即每调用一个函数,如果里面还有新的函数,那么就先把它放到调用堆栈里,等到所有任务都放满以后,开始依次执行。

而函数调用堆栈是一个典型的栈的数据结构,遵循后进先出原则,当 func1func2func3 依次放进调用栈后, 遵循后进先出原则 ,那么 func3 函数的内容会先被执行,之后是 func2 ,最后是 func1 。这就是函数调用堆栈。

五、写在最后

栈在前端中的应用就讲到这里啦!栈在我们平常的开发中无处不在,我们写的每一个程序,基本上都会用到函数调用堆栈。且在前端的面试中,面试官也很喜欢问深拷贝和浅拷贝,大家可以对这块知识多回顾多实践。

如果有不理解或者有误的地方也欢迎私聊我或加我微信指正~

  • 公众号:星期一研究室
  • 微信:MondayLaboratory

创作不易,如果这篇文章对你有用,记得点个 Star 哦~

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

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

相关文章

计网课设 模拟实验拓扑

一:前言 幸福的是考完两科了&#xff0c;抽出时间赶紧赶赶课设&#xff0c;两周遇到了好多事&#xff0c;开心的&#xff0c;烦心的&#xff0c;一堆堆的压过来&#xff0c;但庆幸自己迷失不太久&#xff0c;又能继续学习了 二&#xff1a;实验概览 1:实验拓扑图 2:实验要求…

快醒醒,C# 9 中又来了一堆关键词 init,record,with

一&#xff1a;背景1. 讲故事.NET5 终于在 2020-08-25 也就是前天发布了第八个预览版&#xff0c;这么多的预览版搞得我都麻木了&#xff0c;接踵而来的就是更多的新特性加入到了 C# 9 中&#xff0c;既然还想呆在这条船上&#xff0c;得继续硬着头皮学习哈&#xff0c;这一篇跟…

7-1 字母统计图 (10 分)(思路+详解)

一&#xff1a;题目 摆放在面前的是一小段英文文章。 afeng希望你能帮他统计一下每个小写字母出现的次数。 最后再以柱状图的形式(参照输出样例)输出出来。 输入格式: 输入第一行为一个正整数N(N<100)&#xff0c;表示文章的行数。 随后为N行文本。 输出格式: 由若干行组…

了解js基础知识中的作用域和闭包以及闭包的一些应用场景,浅析函数柯里化

js基础知识中的作用域和闭包一、作用域1、作用域、自由变量简介&#xff08;1&#xff09;作用域定义&#xff08;2&#xff09;作用域实例演示&#xff08;3&#xff09;自由变量定义&#xff08;4&#xff09;自由变量实例演示2、作用域链简介&#xff08;1&#xff09;作用域…

张朝阳一天只睡4小时?不知道,反正我每天都睡足7小时

这是头哥侃码的第213篇原创周末&#xff0c;一个很久没联系过的朋友突然在微信上发给我一个链接。我打开一看&#xff0c;原来是搜狐老板张朝阳近日发表的一个有关睡眠的神论&#xff0c;大致是说他每天只睡四小时&#xff0c;白天状态还特别好&#xff0c;每天员工到公司的时候…

Istio 1.7——进击的追风少年

2020 年 8 月 21 日&#xff0c;Istio 发布了 1.7 版本。除了介绍新版本的主要更新内容外&#xff0c;本文会重点分析 Istio 团队在产品更新策略上的激进态度和举措。是稳扎稳打做好向后兼容&#xff0c;带给用户所承诺的易用性&#xff1b;还是快刀斩乱麻&#xff0c;做进击的…

7-2 港口审查 (15 分)

一:题目 afeng是一个港口的海关工作人员&#xff0c;每天都有许多船只到达港口&#xff0c;船上通常有很多来自不同国家的乘客。 afeng对这些到达港口的船只非常感兴趣&#xff0c;他按照时间记录下了到达港口的每一艘船只情况&#xff1b;对于第i艘到达的船&#xff0c;他记…

【BCVP更新】StackExchange.Redis 的异步开发方式

有哪些习惯坚持LESS IS MORE,SIMPLER IS BETTER THAN MORE你一定会有很大的收获各种小问题&#xff1f;如果你之前用过Redis的话&#xff0c;肯定会使用过StackExchange.Redis&#xff0c;我之前很久就用过&#xff0c;在.netfw的时候&#xff0c;当时并发还比较小&#xff0c;…

map容器实现一对多

一&#xff1a;需求描述 我们希望一个数字或则其他字符串可以对应 一串数&#xff0c; #include<iostream> #include<map> #include<vector> using namespace std; int main(){map<int,vector<int> > m;map<int,vector<int> >:: i…

解决异步问题,教你如何写出优雅的promise和async/await,告别callback回调地狱!

解决异步问题——promise、async/await一、单线程和异步1、单线程是什么2、为什么需要异步3、使用异步的场景二、promise1、promise的三种状态2、三种状态的表现和变化&#xff08;1&#xff09;状态的变化&#xff08;2&#xff09;状态的表现3、then和catch对状态的影响&…

使用 Visual Studio 2019 批量添加代码文件头

应用场景介绍在我们使用一些开源项目时&#xff0c;基本上都会在每个源代码文件的头部看到一段版权声明。一个项目或解决方案中源代码文件的个数少则几十&#xff0c;多则几千甚至更多&#xff0c;那么怎么才能给这么多文件方便地批量添加或者修改一致的文件头呢&#xff1f;在…

7-3 模板题 (10 分)(思路+详解)

一:题目 二&#xff1a;思路 1.读题读不懂&#xff0c;那就分析给出的示例&#xff0c;本题意思就是给出一串数&#xff0c;然后找出找出该元素之后&#xff0c;第一个大于 该元素的下标&#xff08;这一串数的下标是从一开始的&#xff09;如果找不到比起大的&#xff0c;那就…

提升对前端的认知,不得不了解Web API的DOM和BOM

了解Web API的DOM和BOM引言正文一、DOM操作1、DOM的本质2、DOM节点操作&#xff08;1&#xff09;property形式&#xff08;2&#xff09;attribute形式3、DOM结构操作&#xff08;1&#xff09;新增/插入节点&#xff08;2&#xff09;获取子元素列表&#xff0c;获取父元素&a…

Dapr微服务应用开发系列1:环境配置

题记&#xff1a;上篇Dapr系列文章简要介绍了Dapr&#xff0c;这篇来谈一下开发和运行环境配置本机开发环境配置安装Docker为了方便进行Dapr开发&#xff0c;最好&#xff08;其实不一定必须&#xff09;首先在本机&#xff08;开发机器&#xff09;上安装Docker。安装方式可以…

leetcode704二分法:(左闭右闭+左闭右开)

前言 又重温了一遍<肖生客的救赎> 其中安迪的一句话一直回荡我的脑中&#xff1a;“人生可以归结为一种简单的选择&#xff1a;不是忙着活&#xff0c;就是忙着死。” 多深刻&#xff0c;多简单&#xff0c;又多令人深省&#xff0c; 哪有那么多选择 哪有那么多时间去花…

你真的理解事件绑定、事件冒泡和事件委托吗?

一文了解Web API中的事件绑定、事件冒泡、事件委托引言正文一、事件绑定1、事件和事件绑定时什么&#xff1f;2、事件是如何实现的&#xff1f;二、事件冒泡1、事件模型2、事件模型解析&#xff08;1&#xff09;捕获阶段&#xff08;2&#xff09;目标阶段&#xff08;3&#…

欢迎来到 C# 9.0(Welcome to C# 9.0)

翻译自 Mads Torgersen 2020年5月20日的博文《Welcome to C# 9.0》&#xff0c;Mads Torgersen 是微软 C# 语言的首席设计师&#xff0c;也是微软 .NET 团队的项目群经理。C# 9.0 正在成形&#xff0c;我想和大家分享一下我们对下一版本语言中添加的一些主要特性的想法。对于 C…

367. 有效的完全平方数(二分法)

一&#xff1a;题目 二:思路 完全平方数:若一个数能表示成某个整数的平方的形式&#xff0c;则称这个数为完全平方数 思路:1.我们将num先折半,因为它是某个整数的平方&#xff0c;而这个数的范围肯定不会超过num的一半 2.那么这就相当于在[left,num/2]中查找某个数&#xff0c…

译 | Azure 应用服务中的程序崩溃监控

点击上方蓝字关注“汪宇杰博客”原文&#xff1a;Yun Jung Choi, Puneet Gupta翻译&#xff1a;汪宇杰应用程序崩溃经常发生。崩溃是指代码中的异常未得到处理并终止进程。这些未处理的异常也称为二次机会异常&#xff08;second chance exceptions&#xff09;。当您的应用程序…

使用Seq搭建免费的日志服务

Seq简介Seq是老外开发的一个针对.NET平台非常友好的日志服务。支持容器部署&#xff0c;提供一个单用户免费的开发版本。官网&#xff1a;https://datalust.co/seq使用文档&#xff1a;https://docs.datalust.co/docsSeq主体功能如下所示&#xff1a;支持主流的编程语言&#x…