普通方法定义栈

使用基本的JS语法定义栈

  1. // 定义栈
  2. function Stack() {
  3. // 因为栈是通过数值定义的
  4. this.items = [];
  5. }
  6. // 在原型上定义栈的方法
  7. Stack.prototype.push = function(elem) {
  8. this.items.push(elem);
  9. }
  10. Stack.prototype.pop = function() {
  11. return this.items.pop();
  12. }
  13. Stack.prototype.peek = function() {
  14. return this.items[this.items.length - 1];
  15. }
  16. Stack.prototype.isEmpty = function() {
  17. return this.items.length === 0 ? true : false;
  18. }
  19. Stack.prototype.clear = function() {
  20. this.items.length = 0;
  21. }
  22. Stack.prototype.size = function() {
  23. return this.items.length;
  24. }
  25. var a = new Stack();
  26. a.push(1);
  27. a.push(2);
  28. a.push(3);
  29. a.push(4);
  30. // a 可以访问到原数组
  31. a.items.push(123);
  32. console.log(a);

输出结果
image.png

出现的问题

不仅 a 可以直接使用 push , 并且能够直接访问到 items, 原本数组 ,并且可以通过原本数组的方法 push 也能实现添加 a.items.push(123); 因此这种方法不严谨, 具有漏洞

能够解决问题,主要是因为 a 能够访问到 items 原数组, 并且能够使用数组原生的方法。
解决:

    1. 私有属性
    1. Symbol 定义原数组,实例化的变量是访问不到 Symbol 的成员的

使用Symbol 定义栈

  1. // 使用 Symbol 创建
  2. var _items = Symbol(); // 访问属性 _items
  3. function Stack() {
  4. // 因为栈是通过数值定义的
  5. // this.items = [];
  6. // 使用Symbol的属性,创建数组
  7. this[_items] = []
  8. }
  9. // 在原型上定义栈的方法
  10. Stack.prototype.push = function(elem) {
  11. this[_items].push(elem);
  12. }
  13. Stack.prototype.pop = function() {
  14. return this[_items].pop();
  15. }
  16. Stack.prototype.peek = function() {
  17. return this[_items][this[_items].length - 1];
  18. }
  19. Stack.prototype.isEmpty = function() {
  20. return this[_items].length === 0 ? true : false;
  21. }
  22. Stack.prototype.clear = function() {
  23. this[_items].length = 0;
  24. }
  25. Stack.prototype.size = function() {
  26. return this[_items].length;
  27. }
  28. var a = new Stack();
  29. a.push(1);
  30. a.push(2);
  31. a.push(3);
  32. a.push(4);
  33. // console.log(a.Symbol()); // 改为Symbol后,a访问不了Symbol成员
  34. // 使用Symbol还具有漏洞
  35. // ES6 可以通过方法可以获取到Symbol的属性
  36. console.log(Object.getOwnPropertySymbols(a)); // 能够访问到Symbol
  37. var key = Object.getOwnPropertySymbols(a)[0];
  38. console.log(key);
  39. // 拿到key 之后 就可以使用 Symbol定义的数组的push 方法
  40. a[key].push(456);
  41. console.log(a); // 能够可以添加
  42. // for (var i in a) {
  43. // console.log(i);
  44. // }

输出结果
image.png

出现的漏洞问题

ES6 可以通过方法可以获取到Symbol的属性, Object.getOwnPropertySymbols(a) 能够访问到Symbol 的成员 , 然后保存key , a可以通过key访问到 Symbol 的原数组。

使用 WeakMap定义栈

因为 WeakMap 是弱引用,这意味着在没有其他引用存在时垃圾回收能正确进行。和 WeakMap的一些特性,是用 WeakMap 更好的定义栈

  1. // 3 使用 WeakMap 弱引用
  2. var _items = new WeakMap();
  3. function Stack() {
  4. // 因为栈是通过数值定义的
  5. // this.items = [];
  6. // 使用Symbol的属性,创建数组
  7. // this[_items] = []
  8. // 3. 创建数组. 使用 set方法 key 传入this , value传入 []
  9. _items.set(this, []);
  10. }
  11. // 在原型上定义栈的方法
  12. Stack.prototype.push = function(elem) {
  13. // 通过 get 方法 读取this, 通过 push 添加
  14. _items.get(this).push(elem);
  15. }
  16. Stack.prototype.pop = function() {
  17. return _items.get(this).pop();
  18. }
  19. // 返回栈顶元素
  20. Stack.prototype.peek = function() {
  21. return _items.get(this)[_items.get(this).length - 1];
  22. }
  23. // 判断栈是否为空
  24. Stack.prototype.isEmpty = function() {
  25. return _items.get(this).length === 0 ? true : false;
  26. }
  27. Stack.prototype.clear = function() {
  28. _items.get(this).length = 0;
  29. }
  30. Stack.prototype.size = function() {
  31. return _items.get(this).length;
  32. }
  33. var a = new Stack();
  34. a.push(1);
  35. a.push(2);
  36. a.push(3);
  37. a.push(4);
  38. a.push(5);
  39. console.log(a);
  40. console.log(a.pop());
  41. console.log(a.peek());
  42. console.log(a.isEmpty());
  43. a.clear();
  44. console.log(a.isEmpty());
  45. console.log(a.size());
  46. // 使用 栈的方法都是没有问题的

使用 以上定义的方法, 因为WeakMap 是全局变量,在调用函数是会产生闭包

对代码优化 , 使用立即执行函数 ()()

// 使用立即执行函数

var Stack = (function() {
  var _items = new WeakMap();
  // 定义的方法
  var Stack = function() {
    // 因为栈是通过数值定义的 
    // this.items = [];
    // 使用Symbol的属性,创建数组
    // this[_items] = []

    // 3. 创建数组. key 传入this , value传入 []
    _items.set(this, []);
  }
  // 在原型上定义栈的方法
  Stack.prototype.push = function(elem) {
    // 读取this, 通过 push 添加
    _items.get(this).push(elem);
  }

  Stack.prototype.pop = function() {
    return _items.get(this).pop();
  }

  // 返回栈顶元素
  Stack.prototype.peek = function() {
    return _items.get(this)[_items.get(this).length - 1];
  }

  // 判断栈是否为空
  Stack.prototype.isEmpty = function() {
    return _items.get(this).length === 0 ? true : false;
  }

  Stack.prototype.clear = function() {
    _items.get(this).length = 0;
  }

  Stack.prototype.size = function() {
    return _items.get(this).length;
  }

  return Stack;
})()

在进行对代码进行优化, 使用 ES6 class 类 和执行函数。

// 使用 ES6 的class类 和 立即执行函数 

var Stack = (function() {
  var _items = new WeakMap();
  return class Stack {
    constructor() {
      // 定义初始化数据
      _items.set(this, []);
    };
    // 定义方法
    push(elem) {
      // 读取this, 通过 push 添加
      _items.get(this).push(elem);
    }

    pop() {
      return _items.get(this).pop();
    }

    // 返回栈顶元素
    peek() {
      return _items.get(this)[_items.get(this).length - 1];
    }

    // 判断栈是否为空
    isEmpty() {
      return _items.get(this).length === 0 ? true : false;
    }

    clear() {
      _items.get(this).length = 0;
    }

    size() {
      return _items.get(this).length;
    }

    toString() {
      // 获取toString 方法
      return _items.get(this).toString();
    }
  };
})()

输出结果

image.png