栈在前端中的应用,顺便再了解下深拷贝和浅拷贝!
詳解棧在前端中的應用
- 一、棧是什么
- 二、棧的應用場景
- 三、前端與棧:深拷貝與淺拷貝
- 1、JS數據類型
- (1)js數據類型的分類
- (2)js數據類型的定義和存儲方式
- (3)js數據類型的判斷方式
- 2、深究淺拷貝和深拷貝
- (1)淺拷貝
- (2)深拷貝
- 四、前端與棧:函數調用堆棧
- 五、寫在最后
棧 在日常生活中的應用非常廣泛,比如我們最熟悉不過的十進制轉二進制、迷宮求解等等問題。同時,它在前端中的應用也非常廣泛,很多小伙伴都會誤以為 棧 在前端中的應用很少,但殊不知的是,我們寫的每一個程序,基本上都會用到 棧 這個數據結構。比如,函數調用堆棧、數據的深拷貝和淺拷貝……。
所以呢,對于一個前端工程師來說, 棧 結構是一個必學的知識點。在接下來的這篇文章中,將講解關于 棧 在前端中的應用。
一、棧是什么
- 棧是一種只能在表的一端(棧頂)進行插入和刪除運算的線性表;
- 只能在棧頂運算,且訪問結點時依照后進先出 (LIFO) 或先進后出 (FILO) 的原則。
二、棧的應用場景
- 需要后進先出的場景;
- 比如:十進制轉二進制、迷宮求解、馬踏棋盤、判斷字符串是否有效、函數調用堆棧……。
三、前端與棧:深拷貝與淺拷貝
1、JS數據類型
談到堆棧,我們需要先來了解一下關于 js 的兩種數據類型。
(1)js數據類型的分類
首先,JavaScript中的數據類型分為基本數據類型和引用數據類型。
了解完分類以后,相信很多小伙伴心里有一個疑惑:這兩個數據類型是什么呢?且在內存中是存放在哪里呢?
(2)js數據類型的定義和存儲方式
基本數據類型:
基本數據類型,是指 Numer 、 Boolean 、 String 、 null 、 undefined 、 Symbol(ES6新增的) 、 BigInt(ES2020) 等值,它們在內存中都是存儲在 棧 中的,即直接訪問該變量就可以得到存儲在 棧 中的對應該變量的值。
若將一個變量的值賦值給另一個變量,則這兩個變量在內存中是獨立的,修改其中任意一個變量的值,不會影響另一個變量。這就是基本數據類型。
引用數據類型:
那引用數據類型呢,是指 Object 、 Array 、 Function 等值,他們在內存中是存在于 棧和堆 當中的,即我們要訪問到引用類型的值時,需要先訪問到該變量在 棧 中的地址(這個地址指向堆中的值),然后再通過這個地址,訪問到存放在 堆 中的數據。這就是引用數據類型。
這樣說可能有點抽象,讓我們用一張圖來理解一下。
從上圖中可以看到, name 和 age 的值都是基本數據類型,所以他們指向程序中 棧 的位置。而 like 是數組類型,也就是引用數據類型,所以在 棧 中,它先存放了一個 like 的地址,之后再把 like 對應的值,存放到 堆 當中。
了解完數據類型和其存儲方式后,在面試中,還有可能被問到如何判斷某一個數據的類型是什么?什么意思呢?比如說,給你一個數字 7 ,需要你來判斷它是什么,我們都知道它是Number類型,但很多時候止步于如何做才能判斷它是一個Number類型。接下來將詳細介紹三種判斷數據類型的方法。
(3)js數據類型的判斷方式
常用判斷方式:typeof、instanceof、===
1)typeof:
定義:返回數據類型的字符串表達(小寫)
用法:typeof + 變量
可以判斷:
- undefined / 數值 / 字符串 / 布爾值 / function ( 返回 undefined / number / string / boolean / function )
- null 、 object 與 array (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 , Array 與 Function
-
判斷 String ,Number ,Boolean 這三種類型的數據時,直接賦值為 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 數組,我們將它視為一個地址,這個地址存放于 棧 當中,同時,這個地址里面的數據,就指向于 堆 當中。我們來看一下圖例。
從上圖中可以看到,當對 a 中 like 的數據進行改變時,它對應的數據在 堆 中改變。而 b 拷貝后的 like 地址所指向的數據,也是跟 a 一樣在 堆 中的位置。也就是說,a 和 b 中的 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 的執行環境是單線程的。所謂單線程是指一次只能完成一個任務,如果有多個任務,就必須排隊,只有當前面一個任務完成時,才能執行后面一個任務,以此類推。上圖中所演示的,即每調用一個函數,如果里面還有新的函數,那么就先把它放到調用堆棧里,等到所有任務都放滿以后,開始依次執行。
而函數調用堆棧是一個典型的棧的數據結構,遵循后進先出原則,當 func1 , func2 , func3 依次放進調用棧后, 遵循后進先出原則 ,那么 func3 函數的內容會先被執行,之后是 func2 ,最后是 func1 。這就是函數調用堆棧。
五、寫在最后
棧在前端中的應用就講到這里啦!棧在我們平常的開發中無處不在,我們寫的每一個程序,基本上都會用到函數調用堆棧。且在前端的面試中,面試官也很喜歡問深拷貝和淺拷貝,大家可以對這塊知識多回顧多實踐。
如果有不理解或者有誤的地方也歡迎私聊我或加我微信指正~
- 公眾號:星期一研究室
- 微信:MondayLaboratory
創作不易,如果這篇文章對你有用,記得點個 Star 哦~
總結
以上是生活随笔為你收集整理的栈在前端中的应用,顺便再了解下深拷贝和浅拷贝!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 『软件测试3』八大典型的黑盒测试方法已来
- 下一篇: 2017年html5行业报告,云适配发布