准备工作
- 在目录下创建两个文件,分别是index.js和.babelrc
js
复制代码
class Parent {
static type = 'Parent';
#salary = 0;
name = '';
static showType() {
console.log(this.type)
}
#seeSalary() {
console.log(this.#salary)
}
speakName() {
this.#seeSalary()
console.log(this.name)
}
}
class Child extends Parent {
constructor(name) {
super();
this.name = name;
}
speakName() {
super.speakName();
}
}
babelrc
复制代码
{
"presets": [
"@babel/preset-env"
]
}
shell
复制代码
npm install @babel/core @babel/cli @babel/preset-env --save-dev
npx babel index.js -o dist.js
编译后私有变量和方法怎么存储?
js
复制代码
var _salary = new WeakMap();
var _seeSalary = new WeakSet();
var Parent = function () {
function Parent() {
_classPrivateMethodInitSpec(this, _seeSalary);
_classPrivateFieldInitSpec(this, _salary, {
writable: true,
value: 0
});
}
}
function _seeSalary2() {
console.log(_classPrivateFieldGet(this, _salary));
}
function _classPrivateMethodInitSpec(obj, privateSet) {
privateSet.add(obj);
}
function _classPrivateFieldInitSpec(obj, privateMap, value) {
privateMap.set(obj, value);
}
- 总结这部分代码:私有变量会存储在weakMap中,键是对象,值是变量值;私有方法存储在weakSet中,键是对象。对于方法存储了对象还不够,执行方法是需要函数体的,函数体定义在外部。
- 问题是,我调用的是
seeSalary
又不是seeSalary2
。首先要说的是,私有方法只能在类方法中调用,外部是没办法调用的,那么在方法中调用的时候会对调用seeSalary
进行拦截去执行seeSalary2
。
- 还有一个问题,调用babel转换为es5的语法,怎么还有
weakMap
和weakSet
呢?Babel 是包含编译和polyfill两部分的。
编译后静态或公开变量和方法怎么存储?
js
复制代码
var Parent = function() {
function Parent() {
...
_defineProperty(this, "name", '');
}
// 第一个参数是public方法,第二个参数是static方法
_createClass(Parent, [{
key: "speakName",
value: function speakName() {
_classPrivateMethodGet(this, _seeSalary, _seeSalary2).call(this);
console.log(this.name);
}
}], [{
key: "showType",
value: function showType() {
console.log(this.type);
}
}]);
return Parent;
}
_defineProperty(Parent, "type", 'Parent');
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
Object.defineProperty(Constructor, "prototype", { writable: false });
return Constructor;
}
function _classPrivateMethodGet(receiver, privateSet, fn) {
if (!privateSet.has(receiver)) {
throw new TypeError("attempted to get private field on non-instance");
}
return fn;
}
- 总结这部分代码:public的属性会定义在实例自身上,public的方法会定义在构造器的
prototype
身上;static的属性和方法都会定义在类自身上。
那么继承是怎么实现的?
js
复制代码
var Child = function(_Parent2) {
_inherits(Child, _Parent2);
function Child(name) {
var _this;
_this = _callSuper(this, Child);
_this.name = name;
return _this;
}
_createClass(Child, [{
key: "speakName",
value: function speakName() {
_get(_getPrototypeOf(Child.prototype), "speakName", this).call(this);
}
}]);
return Child;
}(Parent);
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } });
Object.defineProperty(subClass, "prototype", { writable: false });
if (superClass) _setPrototypeOf(subClass, superClass);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf ?
Object.setPrototypeOf.bind() :
function _setPrototypeOf(o, p) { o.__proto__ = p; return o; };
return _setPrototypeOf(o, p);
}
function _callSuper(t, o, e) {
return o =
_getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ?
Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) :
o.apply(t, e));
}
- 总结这部分代码:继承是通过修改子类的
prototype
指向了一个新对象,新对象的prototype
指向了父类的prototype
,且新对象的constructor
保持子类的constructor
不变且允许外部代码修改。然后将子类的prototype
拒绝被赋值为其他对象。最后将子类的__proto__
指向父类。
- 有个问题,为什么要创建一个新对象而不是让子类的
prototype
直接指向父类的prototype
呢?这是因为在之前代码中给子类prototype
添加公开方法的时候避免影响父类。
- 还有一个问题,为什么需要
__proto__
指向父类呢?这是为了静态属性和方法也能让子类调用到父类的,前面也提到了静态的方法和属性都是挂载到类自身。
拓展:原型链
- 每个对象(数组、函数等)都有
__proto__
属性,通过该属性指向其他对象串联出原型链
- 函数不仅仅有
__proto__
还有prototype
,但是寻找原型链并不会经过prototype
,除非你是new了一个类,因为new关键字将类的prototype
作为实例__proto__
的值了。
例子
js
复制代码
function P() {}
P.prototype.x = 'x'
function C() {}
C.prototype = P.prototype
console.log(C.x) // undefined
js
复制代码
function P() {}
P.prototype.x = 'x'
P.x = 'xxx'
function C() {}
C.__proto__ = P;
console.log(C.x) // xxx