前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >第七十九期:数据结构(栈)

第七十九期:数据结构(栈)

作者头像
terrence386
发布2022-07-15 10:57:35
1750
发布2022-07-15 10:57:35
举报
文章被收录于专栏:JavaScript高级程序设计

栈是一种很常见的数据结构。先进后出即last in first out 是她的特性。有一些语言内置了栈的数据结构,比如c。但是js中并没有内置这种数据结构,但是这并不影响我们进行开发,我们可以自己实现,并且运用它。

栈的方法一般有这几个:

  • push
  • pop
  • peek
  • clear
  • size

栈的结构则如图所示:

书上的demo一般都用数组来实现栈。但是我们很少去想一个问题,为什么数组完全可以实现栈的所有功能,但是还要去实现栈呢?实现栈的真正的目的是什么呢?

因为栈这种结构,使得我们的应用程序更加语义化。假设我们有一个书包和一个钱包。我们可以把钱放在书包里,也可以把钱放在钱包里。但是放在书包里的话,从外面看,我们不一定知道它里面装的是钱。但是放在钱包里,我们一眼就能知道它里面装的是钱,甚至知道它装的是人民币还是美元。

另外,原生的数组API具有不同的时间复杂度。比如array.prototype.slice在最坏的情况下时间复杂度为o(n),因为它需要遍历所有的索引,并且在数组中拼接时重新进行调整。而push方法在内存缓冲区满的时候,复杂度也为o(n)。

而栈这种结构避免了直接访问元素,在内部依赖于WeakMap(),这种内存效率是很高的。

栈的实现

我们可以是使用es6的方法来实现它。

代码语言:javascript
复制
const sKey = {}
const items = new WeakMap()

class Stack{
  constructor(){
    item.set(sKey,[])
  }
  
  push(element){
    let stack = items.get(sKey)
    stack.push(element)
  }
  pop(){
    let stack = item.get(sKey)
    return stack.pop()
  }
  clear(){
    items.set(sKey,[])
  }
  size(){
    return items.get(sKey).length
  }
}

然后我们可以这样写:

代码语言:javascript
复制
let Stack = (()=>{
  const sKey = {}
  const items = new WeakMap()

  class Stack{
    constructor(){
      items.set(sKey,[])
    }

    push(element){
      let stack = items.get(sKey)
      stack.push(element)
    }
    pop(){
      let stack = items.get(sKey)
      return stack.pop()
    }
    peek(){
      let stack = items.get(sKey)
      return stack[stack.length-1]
    }
    clear(){
      items.set(sKey,[])
    }
    size(){
      return items.get(sKey).length
    }
  }
  return Stack
})()

我们把stack类封装在一个匿名函数中,并且这个函数是个立即调用函数。

这种写法之前我一直很疑惑,为什么要这样写?其实就是为了防止变量的污染,定义的sKey,和items 只有内部的class类可以用,其他地方都用不了。

当然,我们也可以直接定义到类的构造函数中:

代码语言:javascript
复制
constructor(){
  this.sKey = {}
  this.items = new WeakMap()
  this.items.set(sKey,[])
}

这种写法会使得items在外部也可以被访问。

全局使用

想要在全局使用我们定义的stack类,如果我们直接引用,多少显的有点low,我们可以写个装b的写法,判断一下运行环境,然后直接给它添加到全局。

这样就可以避免了复制一堆重复的代码,顺带着也做了运行环境的兼容。

代码语言:javascript
复制
// AMD
if(typeof define === 'function' && define.amd){
  define(function(){return Stack});
  // NodeJs/CommonJs
}eles if(typeof exports === 'object'){
  if(typeof module ==='object' && typeof module.exports==='object'){
  exports = module.exports = Stack;
  }
  // 浏览器
}else{
  window.Stack = Stack
}

这样写是不是好多了?兼容了多个环境,还显得我们的代码有水平,但更重要的是这种思维确实可以提高我们的编码水平。

实际应用

实际应中的场景,在前端业务开发中似乎也并不常见。但是在vue-router的源码的实现中,抽象history的实现用了栈的概念,但是本质上还是用数组实现的这个功能。

代码语言:javascript
复制
export class AbstractHistory extends History {
  index: number
  stack: Array<Route>

  constructor (router: Router, base: ?string) {
    super(router, base)
    this.stack = []
    this.index = -1
  }

  push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    this.transitionTo(
      location,
      route => {
        this.stack = this.stack.slice(0, this.index + 1).concat(route)
        this.index++
        onComplete && onComplete(route)
      },
      onAbort
    )
  }
}

剩下的场景就是在编译解析的过程中会用到栈,因为栈的性能相对数组会好很多。

关于栈,先说这么多吧,等我找个比较好的例子再具体的解释一下具体的场景和使用过程。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-03-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 JavaScript高级程序设计 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 栈的实现
  • 全局使用
  • 实际应用
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档