说在前面
- ES5和ES6如何实现类
- ES5和ES6如何实现私有属性
- 性能
代码
- ES5版本:
function Stack() {
let items = [];
//添加元素入栈
this.push = function (ele) {
items.push(ele);
};
// 弹出栈顶元素
this.pop = function () {
return items.pop();
};
// 查看栈顶元素
this.peek = function () {
return items[items.length-1];
};
// 判断栈顶是否为空
this.isEmpty = function () {
return items.length == 0;
};
// 返回元素个数
this.size = function () {
return items.length;
};
// 清除栈
this.clear = function () {
items = [];
};
// 打印栈内元素
this.print = function () {
console.log(items.toString());
};
}
ES6版本
ES6的类是基于原型的,比基于函数的类更节省内存。
class Stack {
constructor() {
this.items = [];
}
push(ele) {
this.items.push(ele);
}
pop() {
return this.items.pop();
}
peek() {
return this.items[this.items.length-1];
}
isEmpty() {
return this.items.length == 0;
}
size() {
return this.items.length;
}
clear() {
this.items = [];
}
print() {
console.log(this.items.toString());
}
}
测试代码
let stack = new Stack();
console.log(stack.isEmpty());
stack.push(1);
stack.push(2);
stack.push(3);
stack.print();
console.log("stack peek",stack.peek());
console.log("stack size",stack.size());
console.log("return pop",stack.pop());
console.log("after pop",stack.size());
stack.clear();
console.log("after clear",stack.size());
输出:
F:\js>node test.js
true
1,2,3
stack peek 3
stack size 3
return pop 3
after pop 2
after clear 0
改进
不管是ES5还是ES6,items
总是暴露给外界的,是公共的,这不符合面向对象的要求。利用ES6中的Symbol
基本类型,因为它是不可变的,可以用作对象的属性,从有创建“假的私有属性”。只需要把this.items改成this[_items]
let _items = Symbol();
class Stack {
constructor() {
this[_items] = [];
}
push(ele) {
this[_items].push(ele);
}
pop() {
return this[_items].pop();
}
peek() {
return this[_items][this[_items].length-1];
}
isEmpty() {
return this[_items].length == 0;
}
size() {
return this[_items].length;
}
clear() {
this[_items] = [];
}
print() {
console.log(this[_items].toString());
}
}
然而,还是可以通过一些方法破坏这种私有封装,ES6新增的Object.getOwnPropertySymbols();
方法不是吃素的。
console.log("stack peek",stack.peek());
console.log("stack size",stack.size());
console.log("return pop",stack.pop());
console.log("after pop",stack.size());
stack.clear();
console.log("after clear",stack.size()); //0
//获取symbol属性
let objectsSymbols = Object.getOwnPropertySymbols(stack);
console.log(objectsSymbols[0]);
stack[objectsSymbols[0]].push(4);
stack.print();//4
那,我们是不是就没有其他办法来实现真正的私有属性呢?答案显然是肯定的!ES6提供有一种数据类型是可以实现私有的,那就是WeakMap
。
// 使用闭包函数将stack类包起来,items只能在函数内访问,从而保证私有。
Stack = (function () {
const items = new WeakMap();
class Stack {
constructor() {
items.set(this,[]);
}
push(ele) {
let s = items.get(this);
s.push(ele);
}
pop() {
return items.get(this).pop();
}
peek() {
return items.get(this)[items.get(this).length-1];
}
isEmpty() {
return items.get(this).length == 0;
}
size() {
return items.get(this).length;
}
clear() {
items.set(this,[]);
}
print() {
console.log(items.get(this).toString());
}
}
return Stack;
})();
// 使用栈
let stack = new Stack();
console.log(stack.isEmpty());
stack.push(1);
stack.push(2);
stack.push(3);
stack.print();
console.log("stack peek",stack.peek());
console.log("stack size",stack.size());
console.log("return pop",stack.pop());
console.log("after pop",stack.size());
stack.clear();
console.log("after clear",stack.size());
测试
F:\js>node test.js
true
1,2,3
stack peek 3
stack size 3
return pop 3
after pop 2
after clear 0
小结
虽然ES6引入了类的语法,但是JS这门语言还是没有像其他语言那样可以生命私有属性的语法,但是可以通过JS的一些特性帮助完成这一个过程,例如函数闭包。使用ES6的语法实现类,使得编程更加简洁美观,从性能上比使用函数实现的类要好,因为其内存开销更少。