- ES6 的类,可以看作 ES5 构造函数 的另一种写法。
MyGood.prototype.say = function () { console.log(‘say’) }
let good = new MyGood({name: ‘car’, price: 1000});
/**
- new MyGood({name: ‘car’, price: 1000}) ——>
- {
- proto: MyGood.prototype,
- constructor: MyGood,
- name: ‘car’,
- price: 1000,
- run: () => { console.log(‘run’) }
- } */
// 写成ES6的类 class MyGood { constructor(params) { this.name = params.name || ‘’; this.price = params.price || 0; this.run = () => { console.log(‘run’) } } say() { console.log(‘say’) } }
<a name="cjnA8"></a>
## 静态方法和动态方法
- 静态方法:该方法不会被实例继承,而是直接通过类来调用
- 动态方法:被实例继承,通过实例来调用
```javascript
// 案例二:构造函数的静态方法和动态方法
// ES5的写法
function MyGood(params) {
this.name = params.name || '';
this.price = params.price || 0;
this.run = () => { console.log('run') }
}
// 静态方法:该方法不会被实例继承,而是直接通过类来调用
MyGood.say = function () {
console.log('MyGood say')
}
// 动态方法:被实例继承,通过实例来调用
MyGood.prototype.say = function () {
console.log('instance say')
}
let good = new MyGood({ name: 'car', price: 1000 });
MyGood.say(); // 'MyGood say'
good.say(); // 'instance say'
// ES6的写法
class MyGood {
constructor(params) {
this.name = params.name || '';
this.price = params.price || 0;
this.run = () => { console.log('run') }
}
// 动态方法
say() {
console.log('instance say')
}
// 静态方法
static say() {
console.log('MyGood say')
}
}
静态属性和动态属性
- 静态方法:该属性不会被实例继承,而是直接通过类来获取
- 动态方法:被实例继承,通过实例来获取 ```javascript // ES5的写法 function MyClass() {} MyClass.count = 0;
// ES6的写法 class MyClass { static count = 0; } // 或者 class MyClass {} MyClass.count = 0;
<a name="KZQpU"></a>
## 私有方法和私有属性
- 私有方法和私有属性,是只能在类的内部访问的方法和属性,外部不能访问。
- 这是常见需求,有利于代码的封装,但 ES6 不提供,只能通过变通方法模拟实现
- TS 才有`private 和 public`关键字
<a name="uQJ4L"></a>
### 现有的解决方法
- 命名上加以区分
- 比如加下划线
```javascript
class MyClass {
// 公有方法
say() {
console.log('public say')
}
// 私有方法
__say() {
console.log('private say')
}
}
- 利用
Symbol
值的唯一性,将私有方法的名字命名为一个Symbol值- 但
Reflect.ownKeys()
依然可以拿到它们 ```javascript let s1 = Symbol(‘say’); class MyClass { // 公有方法 say() { console.log(‘public say’) } // 私有方法 s1 { console.log(‘private say’) } }
- 但
// 通过Reflect.ownKeys()获取 Reflect.ownKeys(MyClass.prototype); // [‘say’, Symbol(‘say’)]
<a name="Cnihk"></a>
### 私有属性的提案(未实现)
- 在`属性名/方法名`之前加`#`,表示是类的`私有属性/私有方法`
```javascript
class Foo {
#a;
#b;
constructor(a, b) {
this.#a = a;
this.#b = b;
}
#sum() {
return this.#a + this.#b;
}
printSum() {
console.log(this.#sum());
}
}
类的继承
- ES5 的继承,实质是先创造子类的实例对象
this
,然后再通过Parent.apply(this)
将父类的方法添加到this
上Parent.apply(this)
先写后写,问题不大
- ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到
this
上面(所以必须先调用super方法),然后再用子类的构造函数修改this
```javascript // 案例一:如果子类没有定义constructor方法,这个方法会被默认添加 class Point {}
class ColorPoint extends Point {}
// 等同于 class ColorPoint extends Point { constructor(args) { super(args); } }
```javascript
// 案例二:
// ES5 实现继承(寄生组合式继承)
function Father(props) {
this.name = props.name || '';
this.age = props.age || 0;
}
// 在声明函数的时候,会自动创建一个prototype属性,我们管他叫函数原型对象,一般用来存放实例公用的方法
function Son(props) {
this.money = props.money || 100;
Father.call(this, props); // 写在后面也一样
}
// 用来改变subClass的原型的函数
function inheritPrototype(subClass, superClass) {
subClass.prototype = Object.create(superClass.prototype);
subClass.prototype.constructor = subClass;
}
inheritPrototype(Son, Father);
var test = new Son({ name: 'John', age: 18, money: 50 });
console.log(test instanceof Father);// true
console.log(test instanceof Son); // true
/**
* ES5 中
* 一开始声明Son的时候,自动创建的prototype
* Son.prototype = {
* __proto__: Object.prototype,
* constructor: Son(props) {...},
* }
*
* 后来通过inheritPrototype改变Son的prototype
* Son.prototype = {
* __proto__: Father.prototype,
* constructor: Son
* }
*
* 根据new执行的步骤
* new Son({ name: 'John', age: 18, money: 50 }) ——>
* {
* __proto__: Son.prototype,
* constructor: Son
* name: 'John',
* age: 18,
* money: 50
* }
*/
// ES6实现继承
class Father {
constructor(props) {
this.name = props.name || '';
this.age = props.age || 0;
}
}
class Son extends Father {
constructor(props) {
super(props);
this.money = props.money || 100;
}
}
let test = new Son({ name: 'John', age: 18, money: 50 });
console.log(test instanceof Father);// true
console.log(test instanceof Son); // true
/**
* ES6 中
*
* 声明Father,没使用extends
* Father.prototype = {
* __proto__: Object.prototype,
* constructor: Father
* }
*
* 声明Son时,使用extends Father
* Son.prototype = {
* __proto__: Father.prototype,
* constructor: Son
* }
*
* new Son({ name: 'John', age: 18, money: 50 }) ——>
* {
* __proto__: Son.prototype,
* constructor: Son
* name: 'John',
* age: 18,
* money: 50
* }
*/
- 使用 ES6 的
extends
继承时,父类的静态方法,也会被子类继承 ```javascript // 案例三 class A { static hello() { console.log(‘hello world’); } } class B extends A { }
B.hello() // hello world
<a name="zobdz"></a>
### 细说super
- 如果super作为对象,用在静态方法之中,这时super将指向父类,而不是父类的原型对象
- 如果`super`作为函数,调用时,代表父类的构造函数
- `super`虽然代表了`父类A`的构造函数,但返回的是`子类B`的实例
- 即`super`内部的`this`指的是`B`的实例
- 如果`super`作为对象
- 用在子类静态方法之中,这时`super`将指向父类
- 在子类普通方法之中,这是`super`指向父类的原型对象
```javascript
// 案例一:当super作为函数,调用时
class A {}
class B extends A {
consturctor(params) {
// 此时的super(params) 相当于 A.prototype.constructor.call(this, params)
super(params)
}
}
// 案例二:当super 作为对象时
class A {
static print() { console.log('static', this.x); }
print() { console.log(this.x); }
}
class B extends A {
constructor() {
super();
this.x = 2;
}
m() { super.print(); }
static x = 3
static m() { super.print(); }
}
B.m(); // static 3
let instanceB = new B();
instanceB.m(); // 2