私有属性目前提案已经进行到了Stage 3 阶段,即将落地所以还是很有必要了解下,本文不讲解私有属性如何使用以及需要注意的事项,而是侧重于babel和typescript是怎么实现私有属性的,具体使用方法可以看一下阮一峰的 ES6 入门
在看这篇文章之前可以阅读我之前写的 从 babel 看 class(上)、 从 babel 看 class(下)
示例文件
下面的代码都是通过这个例子的演化而来
class Foo {
#name = 'zhangsan';
age = 17;
getName() {
return this.#name;
}
get #x() {
return this.#name;
}
set #x(value) {
this.#name = value;
}
}
babel
function _classPrivateFieldSet(receiver, privateMap, value) {
var descriptor = privateMap.get(receiver);
if (!descriptor) {
throw new TypeError('attempted to set private field on non-instance');
}
if (descriptor.set) {
descriptor.set.call(receiver, value);
} else {
if (!descriptor.writable) {
throw new TypeError('attempted to set read only private field');
}
descriptor.value = value;
}
return value;
}
function _classPrivateFieldGet(receiver, privateMap) {
var descriptor = privateMap.get(receiver);
if (!descriptor) {
throw new TypeError('attempted to get private field on non-instance');
}
if (descriptor.get) {
return descriptor.get.call(receiver);
}
return descriptor.value;
}
var _name = new WeakMap();
var _x = new WeakMap();
var Foo = /*#__PURE__*/ (function () {
function Foo() {
_classCallCheck(this, Foo);
_x.set(this, {
get: _get_x,
set: _set_x,
});
_name.set(this, {
writable: true,
value: 'zhangsan',
});
_defineProperty(this, 'age', 17);
}
_createClass(Foo, [
{
key: 'getName',
value: function getName() {
return _classPrivateFieldGet(this, _name);
},
},
]);
return Foo;
})();
var _get_x = function _get_x() {
return _classPrivateFieldGet(this, _name);
};
var _set_x = function _set_x(value) {
_classPrivateFieldSet(this, _name, value);
};
为了保持简洁,我直接去掉了不必要的代码,这里 babel 的执行顺序如下
- 首先通过
_classCallCheck方法检查是否为new调用,不是直接抛出错误 - 通过
new WeakMap来存储私有属性 - 通过
_defineProperty来设置实例属性 - 通过
_createClass来设置方法和静态方法
所以这里已经可以得出结论了,babel 的私有属性方法实现就是通过WeakMap来实现的
function _classPrivateFieldGet(receiver, privateMap) {
var descriptor = privateMap.get(receiver);
if (!descriptor) {
throw new TypeError('attempted to get private field on non-instance');
}
if (descriptor.get) {
return descriptor.get.call(receiver);
}
return descriptor.value;
}
上面的方法中额外进行了一些判断,一个个看
- 执行
privateMap.get
进行判断的原因是因为 this 可能会丢失,例如通过解构
const f = new Foo();
const { getName } = f;
getName();
-
descriptor.get判断是因为私有属性可以是
get、set这种形式出现
_classPrivateFieldSet方法跟get获取流程类似就不讲解了。
typescript
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, privateMap) {
if (!privateMap.has(receiver)) {
throw new TypeError("attempted to get private field on non-instance");
}
return privateMap.get(receiver);
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, privateMap, value) {
if (!privateMap.has(receiver)) {
throw new TypeError("attempted to set private field on non-instance");
}
privateMap.set(receiver, value);
return value;
};
var _name;
class Foo {
constructor() {
_name.set(this, 'zhangsan');
this.age = 17;
}
getName() {
return this.;
}
get () { return __classPrivateFieldGet(this, _name); }
set (value) {
__classPrivateFieldSet(this, _name, value);
}
}
_name = new WeakMap();
这里直接把代码编译到es2015我们主要看私有属性在 ts 中怎么实现,而 class 转化降级的方法就是降级成函数所以略过。
_name.set和__classPrivateFieldGet、__classPrivateFieldSet从这三处代码我们可以得出结论,ts 也是通过WeakMap来实现的。
实现的过程跟babel大同小异,就是根据this和变量名进行读取和设置
最后
上面的分析到这里就结束了,如果有什么错误欢迎指出,如果对你有帮助欢迎star。