@bornkiller
2017-11-09T15:40:17.000000Z
字数 3021
阅读 1402
前端编程
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