@bornkiller
2017-11-09T07:40:17.000000Z
字数 3021
阅读 1636
前端编程
JavaScript 名义上并不支持传统的面向对象编程。前端刀耕火种阶段,通过构造函数和原型链继承,能够模拟 OOP ,但语法层面无法通过关键词处理 public field,private field,因而在解决部分实际应用场景时会带来困扰,本文将探讨如何处理这些问题。
先假设场景,需要实现简单的栈(遵循 LIFO 原则的有序数据集合)类,实例代码如下:
/*** @description - implement lite Stack* @author - huang.jian <hjj491229492@hotmail.com>*/export default class Stack {constructor() {this.items = [];}push(item) {this.items.push(item);}pop() {return this.items.pop();}}
/*** @description - consume lite Stack* @author - huang.jian <hjj491229492@hotmail.com>*/import Stack from './lib/Stack';const history = new Stack();history.push('https://www.baidu.com');history.push('https://www.google.com');history.push('https://www.qq.com');console.log(history.items);
上述代码用以说明场景,其满足 LIFO 原则,但问题处在用于内部存储的数组,Stack 实例可以直接访问 items,意味着外部操作极易影响内部稳定,将其转化为 private field,则可以完美解决问题。
团队内部使用同样的约定,通过 eslint,tslint来强制确保执行,例如使用前缀:
/*** @description - implement lite Stack* @author - huang.jian <hjj491229492@hotmail.com>*/export default class Stack {constructor() {this._items = [];}push(item) {this._items.push(item);}pop() {return this._items.pop();}}
优点:
缺点:
如果使用 transpile to javascript 方案,例如 typescript,可以利用其增强语法进行声明:
/*** @description - implement lite Stack* @author - huang.jian <hjj491229492@hotmail.com>*/export default class Stack {private items: any[];constructor() {this.items = [];}public push(item) {this.items.push(item);}public pop() {return this.items.pop();}}
具体实例,对私有变量的访问,将无法通过编译:

优点:
缺点:
应用开发中,通过团队约定的方式,即前述解决方案,能够处理大部分场景。如果安全性要求较高,考虑使用模拟的方式来实现,解决思路主要是避免把 private field 挂载到 this 上。
/*** @description - implement lite Stack* @author - huang.jian <hjj491229492@hotmail.com>*/export default class Stack {constructor() {const items = [];this.push = (item) => {items.push(item);};this.pop = () => {return items.pop();};}}
优点:
缺点:
改造方案如下:
/*** @description - implement lite Stack* @author - huang.jian <hjj491229492@hotmail.com>*/const Stack = (function () {const items = [];class Stack {push(item) {items.push(item);}pop() {return items.pop();}}return Stack;})();export default Stack;
优点:
缺点:
再改造方案如下:
/*** @description - implement lite Stack* @author - huang.jian <hjj491229492@hotmail.com>*/const Stack = (function () {const PrivateStorage = Reflect.construct(WeakMap, []);class Stack {constructor() {PrivateStorage.set(this, {items: []});}push(item) {const PrivateSet = PrivateStorage.get(this);const PrivateSetNext = {...PrivateSet,items: [...PrivateSet.items, item]};PrivateStorage.set(this, PrivateSetNext);}pop() {const PrivateSet = PrivateStorage.get(this);return PrivateSet.items.pop();}}return Stack;})();export default Stack;
多实例 this 指针不同,即可为不同实例存储各自私有数据。
目前已经出现相关提案,https://github.com/tc39/proposal-class-fields,浏览器尚未支持。
/*** @description - implement lite Stack* @author - huang.jian <hjj491229492@hotmail.com>*/export default class Stack {#items;constructor() {this.#items = [];}push(item) {this.#items.push(item);}pop() {return this.#items.pop();}}
Email: hjj491229492@hotmail.com
