栈(stack)
栈是一种很常见的数据结构。栈可以用来在函数调用的时候存储断点,做递归时要用到栈!
栈是一种 先进后出 的一种数据结构。
这就好比一摞书,最底下的把本书是第一个放进去的,取书时一般从顶部第一个开始取,取到最后才会把最底下(那个第一个放的)书取出来。故 “先进后出”。

栈的一些操作方法:
一个栈一般有如下几个方法:
size返回栈的长度push(data)入栈(将数据放入栈顶)pop()出栈(将数据从栈顶拿出)clear()清空栈peek()返回栈顶元素isEmpty()判断是否是空栈栈
当然,第一部肯定是创建一 个栈。这里用 数组和 ES6 中的类来实现。在JavaScript数组中也有 push() 和 pop() 两个方法,因此实现起来很容易
-
数组中每次
push操作就会把最新的数据放到数组最后一项,数组长度自动变化; -
数组中每次
pop操作就会把数组中的最后一项返回出去,长度变小;
利用这两个方法就实现了 先进后出 的目的。
看以下代码:class Stack{
constructor(){
this.items = [];
}
}在栈操作中,我们不希望把
this.items属性暴露出来。这应该怎么做呢?有两种办法: -
利用闭包;
-
使用最新的(相对于写这篇文档) ES 类特性 —— 类的私有属性;
第一种,利用立即执行函数:
let Stack = (function(){
const items = [];
class Stack{
push(data){
items.push(data);
}
pop(){
return items.pop();
}
isEmpty(){
if(items.length === 0){
return true;
}
return false;
}
peek(){
return items[0];
}
size(){
return items.length;
}
clear(){
items.splice(0);
}
}
return Stack;
})();
上面代码中,立即执行函数的最后不要忘了 return Stack; 因为,立即执行函数执行完后应该把 class 类 —— Stack 暴露出来,让我们去 new 。函数中的 items 就可以通过闭包保存下来。clear 操作不应该 items = [] 这样只是把 items 的地址发生了改变,应使用 splice() 方法把其清零。
使用 ES6 中的 WeakMap() 来实现:
let Stack = (function(){
let items = new WeakMap();
class Stack(){
constructor(){
items.set(this,[]);
}
push(data){
let stack = items.get(this);
stack.push(data);
}
pop(){
let stack = items.get(this);
return stack.pop();
}
// ...... 其它方法
}
return Stack;
})();
使用 WeakMap 也是可以的,需要注意的是:WeakMap 数据类型的键只接受对象,刚好 this 是个不错的选择。不好的一点就是操作时,每次都要通过 get() 方法去取数组,这样有点繁杂。
通过 ES 5 实现:
function Stack(){
var items = [];
this.push = function(data){
items.push(data);
}
this.pop = function(){
return items.pop();
}
this.size = function(){
return items.length;
}
this.isEmpty = function(){
if(items.length === 0){
return true;
}
return false;
}
this.peek = function(){
return items[0];
}
this.clear = function(){
items.splice(0);
}
}
通过 ES6 Symbol 来实现:
let Stack = (function(){
const _items = Symbol();
class Stack{
constructor(){
this[_items] = [];
}
push(data){
this[_items].push(data);
}
pop(){
return this[_items].pop();
}
// .....
}
return Stack;
})();
通过,Symbol 方式来实现,并不是很理想的一种做法,因为我们并没有很好的做到属性的私有。因为在ES6新增的 Object.getOwnPropertySymbols() 方
法能够取到类里面声明的所有Symbols属性:
let objSymbols = Object.getOwnPropertySymbols(stack);
console.log(stack[objSymbols[0]]);
// 甚至改变内部数组的结果:
stack[objSymbols[0]].push(10);
最新的方案
在比 ES7 更新的ES版本中,提出了 类私有属性 的语法:
class Stack{
#items;
constructor(){
this.#items = [];
}
push(data){
this.#items.push(data);
}
// ......
}
只需在属性名前加上 # 字符即可实现,在外部不会被访问到。该语法在最新的 Chrome 浏览器上已实现。
栈的应用
一个简单的进制转换方法:把十进制转为 2-16 进制数
function numParse(number,n){
var ary = [0,1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'];
if(number <= 1){
return number;
}
if(number < n){
return 0;
}
var stack = new Stack();
var b = 0,
a = number;
str = '';
while(1){
b = a % n;
stack.push(ary[b]);
a = Math.floor(a / n); // 注意这里应向下取整
if(a < n){
stack.push(ary[a]);
break;
}
}
while(stack.size()){
str += stack.pop();
}
return str;
}
比如一个十进制数 —— 10 转为二进制数:
10 % 2 === 0---> 找到 ary[0] 存入栈;Math.floor(10 / 2) === 5;5 % 2 ===---> 找到 ary[1] 存入栈;Math.floor(5 / 2) === 2;2 % 2 === 0----> 找到 ary[0] 存入栈;Math.floor(2 / 2) === 1;- 此时应作判断:
1 < 2 ?(if(a < n){...}),执行stack.push(ary[a]);然后跳出循环 - 最后通过循环出栈。
一次性添加多个项:
const Stack = (function(){
var items = [];
class Stack{
constructor(...item){
items.push(...item);
}
push(){
// .....
}
}
return Stack;
})();
打印栈元素:
print(){
return items.toString();
}