前端也要会的数据结构 (不按期更新篇)

前端的软肋

一说到前端你们脑子里只有,布局、展现数据、修改样式等等。但是数据是哪里来的呢?后端给的后端给的。数据的结构呢?后端给啥用啥。前端

这就是前端的一个软肋。咱们的业务让咱们并不须要过深刻的了解数据结构,数据结构和算法是一个程序员的基础。不管是前端开发仍是后端开发、仍是AI机器学习大数据,我认为都须要必定的数据结构和算法知识(除了前端,其他的都是强烈的刚需。。。),前端的伙伴们学会数据结构有什么好处呢?改变思考方式,深刻了解js执行的一些过程,在代码中不知不觉考虑代码层面的性能优化。用处不少,接下来开始吧。程序员

什么是数据结构?

数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。(来自百度百科)算法

计算机存储是什么意思呢?就是咱们常说的存储结构。express

组织数据的方式与特定关系呢?就是咱们的逻辑结构。redux

一堆按必定的存储结构和逻辑结构组合起来的数据集合就是数据结构。后端

这是个人一些理解。因此当一种数据结构摆在个人面前我就会考虑,它是以哪一种形式存储起来,它有什么特殊的逻辑组合起来。这种方式有哪些好处呢?当我明白这些的时候,这种数据结构我就算基本了解了。数组

首先咱们先说一下线性表,线性表是数据的逻辑结构,元素之间是一对一的关系,你个线性表中,任何一个元素的前一个或者后一个都只有一个元素,而不会出现任何一个元素的前一个或者后一个元素对应多个元素的状况。浏览器

线性表分为通常性的线性表、与受限的线性表。通常性的线性表就比如数组。就是最多见的通常性线性表,而受限的线性表就是栈与队列。性能优化

好了好了 咱们开始正题了

你好 神奇的栈结构

栈型结构,最大的特色是什么?先进后出,先进入的元素要比后进入的元素更晚的离开这个容器,这像什么一堆摞起来的书籍。你这能拿走书的最上面的。(你要是给书推倒了,那就只能说明你比较睿智)bash

因此咱们要实现一个栈型结构很天然的想到了

容器:数组

方法:数组的push与pop方法 下面开始实现一个栈的构造函数

function Stack(){
    // 用let建立一个私有容器,没法用this选择到dataStore;
    let dataStore = [];
    // 模拟进栈的方法 
    this.push = function(element){
        dataStore.push(element);
    };
    // 模拟出栈的方法,返回值是出栈的元素。
    this.pop = function(){
        return dataStore.pop();
    };
    // 返回栈顶元素
    this.peek = function(){
        return dataStore[dataStore.length-1];  
    };
    // 是否为空栈
    this.isEmpty = function(){
        return dataStore.length === 0 
    };
    // 获取栈结构的长度。
    this.size = function(){
        return dataStore.length;
    };
    //  清除栈结构内的全部元素。
    this.clear = function(){
        dataStore = [];
    }
}
复制代码

好了伙伴们 栈的构造函数咱们已经写好了。

// 一个单独的栈生成了。
let stack = new Stack();
stack.push(1);
stack.push(2);
stack.push(5);
stack.peek(); // return 5
stack.size(); // return 3
stack.clear();
stack.peek(); // undefined
复制代码

一个基本的栈型结构就实现了。 真的非常简单。可是这个东西在js中有什么用处呢?

在js中的用处

用处很大,首先咱们都知道递归若是没有设置好边界值,就会报堆栈溢出。 什么意思??? 咱们的js代码在执行的时候,会生成一个调用栈(里面装满了全部执行中的函数)

在这个调用栈第一个进栈的也就是压在最下面的就是全局函数,这个全局函数只会在浏览器关闭后才会出栈,浏览器关闭了这个堆栈也就随之消失了。 当浏览器执行下去的时候执行一个函数的时候,就会把这个执行中的函数加入到调用栈中,被调用了就要进调用栈(比较好理解吧),同时控制权也会转交给这个函数,这个函数中若是有别的函数,执行到别的函数时,作着同样的事情,进栈。当任何一个函数执行结束以后,那么很差意思,他就要退栈了。离开这个调用栈了。由于调用栈中内容过多,表明着垃圾资源没有回收,从而致使浏览器卡顿,这是不合理的,执行完毕就会退栈。

退栈以后,那么执行权就要交回到包含着它的函数了。

堆栈的三种场景

1:递归是一种怎样的状况呢?

当咱们没有考虑递归的出口的时候,
简化函数
function fn(){
    let a = 1; fn()
}
fn()  // 报错!!!! Maximum call stack size exceeded
      // 最大调用堆栈大小超过
复制代码

当我没有设定出口时,并无任何一个函数会出栈,在不断的循环调用后,你的堆栈确定不会是无限的,那么就只好提醒你堆栈溢出,程序报错。

2: 你知道redux的洋葱模型图吗?

所谓redux的洋葱模型(其实redux我没用过,可是在公司分享会上听过一段对于这个洋葱模型图的分享),你们也能够理解一下express框架的写接口时的next函数。

在咱们洋葱在最外层执行完毕后就会进入里面,到最内部后再循坏退出来。

function fn1(){
    console.log('fn1 first');
    fn2()
    console.log('fn1 last');
}
function fn2(){
    console.log('fn2 first');
    fn3()
    console.log('fn2 last');
}
function fn3(){
    console.log('fn3 first');
    console.log('fn3 last');
}
fn1()
复制代码

打印结果中咱们能够看出,fn1执行的时候在,遇到fn2执行进栈后,将控制权转交给fn2,fn2执行遇到fn3执行进栈,将控制权交给fn3,fn3执行完毕后退栈,控制权还给fn2,那么fn2后面的代码会继续执行,fn2的代码执行完毕,退栈。继续执行fn1后面的代码直到退栈。可能这种简单的模式你们看起来比较清晰,若是有比较复杂的内容,你们记得画图不要弄错了。

3: 不说你就会忘的闭包

为何闭包使用过多会致使程序卡顿,性能很差???

这个问题很让人费解,可是扯上调用栈后,我以为能够解释一波(代码我就不上了,你们能够去找找闭包得代码看一看) 闭包得私有变量怎么产生的? 在一个函数执行后,它执行完毕就必定会退栈。

function A(){
  var count = 0;
  function B(){
    count ++;
    console.log(count);
  }
  return B;
}
var C = A();
C();// 1
C();// 2
C();// 3
复制代码

很差意思我食言了,方便你们理解仍是上一波代码吧。

在A函数执行得时候,咱们发现执行过程当中,遇到B函数,好了它开始调用进栈执行,执行完毕后,控制权回归A函数,而后把B函数return出去了。B函数中保持对count变量的引用,你就把它return出去了????好吧你愿意你就这么干。

B函数被推出去到外面的世界(外面的函数体内);将B赋值给C,好了C须要count变量的支持,count就不能离开内存(也就是不能被垃圾回收);拿咋办? A函数执行完了我也该离开了(函数执行完毕后,函数内部的变量会被回收掉)。很差意思,外面执行着的函数还有对你的引用,那很差意思你别退栈了,并不容许离开调用栈,由于要保留count变量的环境。

好吧每个这种状况就会有几个函数没法退栈,调用栈里面的内容越堆越多。就会越加卡顿。一辆公交车,我们的乘客到站就下车了,可是总有几个乘客死活不下车,车上人愈来愈多,车也愈来愈沉,好吧跑起来就愈来愈慢了。

是时候结个尾了

在不理解栈的时候,我很难去想以上几种状况,数据结构真的在改变个人思路,一切的知识点都在这些基础中有着验证,夯实基础,我认为数据结构也不该该是前端的加分项,而是比会项。最后的最后,我用我听过的一句很经典的话来结尾好了

咱们写的代码不是为了更好的和人去沟通,而是去更好的和机器沟通

最后打个广告(咱们一块儿维护的学习公众号)

公众号主要面向的是初级/应届生。内容包含咱们从应届生转换为职场开发所踩过的坑,以及咱们每周的学习计划和学习总结。 内容会涉及计算机网络算法等基础;也会涉及前端,后台,Android等内容~

求关注,不迷路

咱们基友团其余朋友的文章:

Android基友

Java基友

相关文章
相关标签/搜索