👦个人简介:张清悠,字澄澈,号寻梦客,爱好旅行、运动,主攻前端方向技术研发,副攻Unity 3D、C++、Python人工智能等
📝个人寄语:学会不为过程的缓慢而焦虑,即使暂时未能如你所愿,但只要你在努力,你就在不断成长!
🙏个人公众号:清悠小猿(海量源码尽在其中,欢迎关注)
1. 抽象基类
有时候可能需要定义这样一个类,它可供其他类继承,但本身不会被实例化。虽然 ECMAScript 没
有专门支持这种类的语法 ,但通过 new.target 也很容易实现。new.target 保存通过 new 关键字调
用的类或函数。通过在实例化时检测 new.target 是不是抽象基类,可以阻止对抽象基类的实例化:
抽象基类
class Vehicle {
constructor() {
console.log(new.target);
if (new.target === Vehicle) {
throw new Error('Vehicle cannot be directly instantiated');
class Bus extends Vehicle {
}
new Bus(); // class Bus {}
new Vehicle(); // class Vehicle {}
// Error: Vehicle cannot be directly instantiated
另外,通过在抽象基类构造函数中进行检查,可以要求派生类必须定义某个方法。因为原型方法在
调用类构造函数之前就已经存在了,所以可以通过 this 关键字来检查相应的方法:
抽象基类
class Vehicle {
constructor() {
if (new.target === Vehicle) {
throw new Error('Vehicle cannot be directly instantiated');
}
if (!this.foo) {
throw new Error('Inheriting class must define foo()');
}
console.log('success!');
}
}
派生类
class Bus extends Vehicle {
foo() {
}
}
派生类
class Van extends Vehicle {
}
new Bus(); // success!
new Van(); // Error: Inheriting class must define foo()
2. 继承内置类型
ES6 类为继承内置引用类型提供了顺畅的机制,开发者可以方便地扩展内置类型:
洗牌算法
class SuperArray extends Array {
shuffle() {
for (let i = this.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[this[i], this[j]] = [this[j], this[i]];
}
}
}
let a = new SuperArray(1, 2, 3, 4, 5);
console.log(a instanceof Array); // true
console.log(a instanceof SuperArray); // true 8.4 类 263
console.log(a); // [1, 2, 3, 4, 5]
a.shuffle();
console.log(a); // [3, 1, 4, 5, 2]
有些内置类型的方法会返回新实例。默认情况下,返回实例的类型与原始实例的类型是一致的:
class SuperArray extends Array {
}
let a1 = new SuperArray(1, 2, 3, 4, 5);
let a2 = a1.filter(x => !!(x%2))
console.log(a1); // [1, 2, 3, 4, 5]
console.log(a2); // [1, 3, 5]
console.log(a1 instanceof SuperArray); // true
console.log(a2 instanceof SuperArray); // true
如果想覆盖这个默认行为,则可以覆盖 Symbol.species 访问器,这个访问器决定在创建返回的
实例时使用的类:
class SuperArray extends Array {
**static get [Symbol.species]() {
**
**return Array;**
**}**
}
let a1 = new SuperArray(1, 2, 3, 4, 5);
let a2 = a1.filter(x => !!(x%2))
console.log(a1); // [1, 2, 3, 4, 5]
console.log(a2); // [1, 3, 5]
console.log(a1 instanceof SuperArray); // true
**console.log(a2 instanceof SuperArray); // false**
3. 类混入
把不同类的行为集中到一个类是一种常见的 JavaScript 模式。虽然 ES6 没有显式支持多类继承,但
通过现有特性可以轻松地模拟这种行为。
注意 Object.assign()方法是为了混入对象行为而设计的。只有在需要混入类的行为
时才有必要自己实现混入表达式。如果只是需要混入多个对象的属性,那么使用
Object.assign()就可以了。
在下面的代码片段中,extends 关键字后面是一个 JavaScript 表达式。任何可以解析为一个类或一
个构造函数的表达式都是有效的。这个表达式会在求值类定义时被求值:
class Vehicle {
}
function getParentClass() {
console.log('evaluated expression');
return Vehicle;
class Bus extends **getParentClass()** {
}
}
可求值的表达式
混入模式可以通过在一个表达式中连缀多个混入元素来实现,这个表达式最终会解析为一个可以被
继承的类。如果 Person 类需要组合 A、B、C,则需要某种机制实现 B 继承 A,C 继承 B,而
再继承 C,从而把 A、B、C 组合到这个超类中。实现这种模式有不同的策略。
一个策略是定义一组“可嵌套”的函数,每个函数分别接收一个超类作为参数,而将混入类定义为
这个参数的子类,并返回这个类。这些组合函数可以连缀调用,最终组合成超类表达式:
class Vehicle {
}
let FooMixin = (Superclass) => class extends Superclass {
foo() {
console.log('foo');
}
};
let BarMixin = (Superclass) => class extends Superclass {
bar() {
console.log('bar');
}
};
let BazMixin = (Superclass) => class extends Superclass {
baz() {
console.log('baz');
}
};
class Bus extends FooMixin(BarMixin(BazMixin(Vehicle))) {
}
let b = new Bus();
b.foo(); // foo
b.bar(); // bar
b.baz(); // baz
通过写一个辅助函数,可以把嵌套调用展开:
class Vehicle {
}
let FooMixin = (Superclass) => class extends Superclass {
foo() {
console.log('foo');
}
};
let BarMixin = (Superclass) => class extends Superclass {
bar() {
console.log('bar');
}
};
let BazMixin = (Superclass) => class extends Superclass {
baz() {
console.log('baz');
}
};
**function mix(BaseClass, ...Mixins) {
**
**return Mixins.reduce((accumulator, current) => current(accumulator), BaseClass);**
**}**
**class Bus extends mix(Vehicle, FooMixin, BarMixin, BazMixin) {
}**
let b = new Bus();
b.foo(); // foo
b.bar(); // bar
b.baz(); // baz
注意 很多 JavaScript 框架(特别是 React)已经抛弃混入模式,转向了组合模式(把方法
提取到独立的类和辅助对象中,然后把它们组合起来,但不使用继承)。这反映了那个众
所周知的软件设计原则:“组合胜过继承(composition over inheritance)。”这个设计原则被
很多人遵循,在代码设计中能提供极大的灵活
总结
```
以上就是今天的JavaScript高级程序设计(二)继承
会持续更新中…
原创不易,期待您的点赞关注与转发评论😜😜