媒介
数组是 JS 中最经常使用的数据构造,它可以在恣意位置增加或删除数据。栈是别的一种数据构造,类似于数组,然则在增加或删除数据时越发天真。
栈数据构造
栈是一种 后进先出(LIFO) 的数据构造。新增加或待删除的元素都保存在栈的一端,叫 栈顶 ,另一端就叫做 栈底 。在栈中,新元素都接近栈顶,就元素都接近栈底。
建立栈
可以用数组来模仿一个栈构造:
function Stack() {
let items = []
// 栈的属性和要领
}
须要完成的要领:
- push(element): 增加一个元素到栈顶
- pop(): 移除栈顶的元素,并返回该元素
- peek(): 仅仅返回栈顶的元素
- clear(): 清空栈
- size(): 返回栈中的元素的个数
- isEmpty(): 推断栈是不是为空
// 向栈中增加元素
this.push = function (element) {
items.push(element)
}
// 从栈中移除元素
this.pop = function () {
return items.pop()
}
// 检察栈顶元素
this.peek = function () {
return items[item.length - 1]
}
// 搜检栈是不是为空
this.isEmpty = function () {
return !!item.length
}
// 清空栈中的元素
this.clear = function () {
items = []
}
// 返回栈的大小
this.size = function () {
return items.length
}
// 打印栈
this.print = function () {
console.log(items.toString())
}
ES6 与 栈
ES6 的写法:
class Stack {
constructor() {
this.items = []
}
push (element) {
this.items.push(element)
}
// ... 其他要领
}
ES6 的类是基于原型的,虽然基于原型的类比基于函数的类更节约内存,然则却不能声明私有变量,所以变量 items 是大众的。这类情况下,可以直接经由过程修正 items 来修正栈中的数据,这是没法防止的。
用 ES6 的限制作用域 Symbol 完成类
ES6 新增了 Symbol 基本范例,它是不可变的,也可以作用对象的属性。
let _items = Symbol()
class Stack {
constructor() {
this[_items] = []
}
// ... 其他要领
}
上面这个例子建立了一个假的私有属性,不能完整躲避上面提到的题目,由于 ES6 新增的 Object.getOwnPropertySymbols
要领可以取到类内里声明的一切 Symbols 属性,比方:
let stack = new Stack()
stack.push(66)
stack.push(88)
let objectSymbols = Object.getOwnPropertySymbols(stack)
console.log(objectSymbols.length) // 1
console.log(objectSymbols[0]) // Symbol()
stack[objectSymbols[0]].push(1)
stack.print() // 66 88 1
经由过程接见 stack[objectSymbols[0]] 是可以接见 _items 的,而且可以对 _items 举行恣意操纵。
用 ES6 的WeakMap 完成类
有一种数据范例可以确保属性是私有的,这就是 WeakMap 。WeakMap 可以存储键值对,其中键是对象,值可所以恣意数据范例。
const items = new WeakMap()
class Stack {
constructor() {
items.set(this, [])
}
push(element) {
let s = items.get(this)
s.push(element)
}
pop() {
let s = items.get(this)
return s.pop()
}
// ... 其他要领
}
如今,Stack 中的 items 是私有的了,然则 items 是在 Stack 类之外声明的,照样可以被修改,所以须要借助闭包来完成一层封装:
let Stack = (function () {
const items = new WeakMap()
class Stack {
constructor() {
items.set(this, [])
}
// ... 其他要领
return Stack
}
})()
### 用栈处置惩罚实际题目
栈在 JS 中运用照样非常普遍的,比方 挪用栈 。进制转换也是很罕见的例子,可以用 栈 来处置惩罚,比方要把十进制转化成二进制,可以将该十进制数字和2整除,直到效果是 0 为止。
function divideBy2 (decNumber) {
var remStack = new Stack(),
rem,
binaryString = ''
while (decNumber > 0) {
rem = Math.floor(decNumber % 2)
remStack.push(rem)
decNumber = Math.floor(decNumber / 2)
}
while (!remStack.isEmpty()) {
binaryString += remStack.pop().toString()
}
return binaryString
}
这个例子中,当效果满足和2做整除的前提是,会获得当前效果和2的余数,放到栈里,然后让效果继承和2做整除。
#### 革新算法
把上面的例子改成十进制转成恣意进制的:
function baseConverter(decNumber, base) {
var remStack = new Stack(),
rem,
binaryString = '',
digits = '0123456789ABCDEF'
while (decNumber > 0) {
rem = Math.floor(decNumber % base)
remStack.push(rem)
decNumber = Math.floor(decNumber / base)
}
while (!remStack.isEmpty()) {
binaryString += digits[remStack.pop()]
}
return binaryString
}