# class 定义方式
- 我们会发现,按照前面的构造函数形式创建 类,不仅仅和编写普通的函数过于相似,而且代码并不容易理解。
- 在 ES6(ECMAScript2015)新的标准中使用了 class 关键字来直接定义类;
- 但是类本质上依然是前面所讲的构造函数、原型链的语法糖而已;
- 所以学好了前面的构造函数、原型链更有利于我们理解类的概念和继承关系;
- 那么,如何使用 class 来定义一个类呢?
- 可以使用两种方式来声明类:类声明和类表达式;
// 类的声明 | |
class Person {} | |
// 类的表达式 | |
// const Animal = class {} | |
// 研究一下类的特性 | |
console.log(Person.prototype) // {constructor: ƒ}/node -> {} | |
console.log(Person.prototype.__proto__) // constructor: ƒ, …}/node -> [Object: null prototype] {} | |
console.log(Person.prototype.constructor) // [class Person] | |
console.log(typeof Person) // function | |
var p = new Person() | |
console.log(p.__proto__ === Person.prototype) // true |
# class 构造函数
-
如果我们希望在创建对象的时候给类传递一些参数,这个时候应该如何做呢?
- 每个类都可以有一个自己的构造函数(方法),这个方法的名称是固定的 constructor;
- 当我们通过 new 操作符,操作一个类的时候会调用这个类的构造函数 constructor;
- 每个类只能有一个构造函数,如果包含多个构造函数,那么会抛出异常;
-
当我们通过 new 关键字操作类的时候,会调用这个 constructor 函数,并且执行如下操作:
- 在内存中创建一个新的对象(空对象);
- 这个对象内部的 [[prototype]] 属性会被赋值为该类的 prototype 属性;
- 构造函数内部的 this,会指向创建出来的新对象;
- 执行构造函数的内部代码(函数体代码);
- 如果构造函数没有返回非空对象,则返回创建出来的新对象;
# class 方法定义
const characters = ['稚名真白', '樱岛麻衣', '薇尔莉特', '和泉纱雾', '小鸟游六花' ] | |
// 类的实例方法 | |
class Characters { | |
constructor(name) { | |
this.name = name | |
this._friends = 'saber' | |
} | |
sleeping(){ | |
console.log(this.name + ' sleeping'); | |
} | |
// 类的访问器方法 | |
get friends() { | |
// 拦截访问器操作 | |
return this._friends | |
} | |
// 拦截设置操作 | |
set friends(newFriends) { | |
this._friends = newFriends | |
} | |
// 类的静态方法 | |
static randomCharacters() { | |
return new Characters ((characters[~~(Math.random() * characters.length)])) | |
} | |
} | |
Object.getOwnPropertyDescriptors | |
const lain = new Characters("lain") | |
console.log(lain) // Person {name: 'lain'} | |
lain.sleeping() | |
lain.friends = 'nekoaimer' | |
console.log(lain) // Character {name: 'lain', _friends: 'nekoaimer'} | |
// 随机打印创建角色 | |
characters.forEach(() => { | |
console.log(Characters.randomCharacters()) | |
}) |
# class 继承与重写
- 我们会发现在下面的代码中我使用了一个 super 关键字,这个 super 关键字有不同的使用方式:
- 注意:在子(派生)类的构造函数中使用 this 或者返回默认对象之前,必须先通过 super 调用父类的构造函数!
- super 的使用位置有三个:子类的构造函数、实例方法、静态方法;
class Characters { | |
constructor(name) { | |
this.name = name | |
} | |
sleeping(){ | |
console.log('Characters sleeping~'); | |
} | |
static sleepingStatic() { | |
console.log(`Characters sleepingStatic~`); | |
} | |
} | |
// Lain 称之为子类 (派生类) | |
class Lain extends Characters { | |
// JS 引擎在解析子类的时候就有要求,如果我们有实现继承 | |
// 那么子类的构造方法中,在使用 this 之前 | |
constructor(name, age) { | |
super(name) | |
this.age = age | |
} | |
drinking() { | |
console.log(`Lain drinking~`); | |
} | |
//sleeping 方法重写 | |
sleeping() { | |
super.sleeping() // 调用父类方法执行逻辑再执行子类执行逻辑 | |
console.log(`Lain sleeping~~~`) | |
} | |
// 重写静态方法 | |
static sleepingStatic() { | |
super.sleepingStatic() // 调用父类静态方法执行逻辑再执行子类静态执行逻辑 | |
console.log('sleepingStatic~~~'); | |
} | |
} | |
const lain = new Lain("lain", 16) | |
lain.sleeping() // Characters sleeping~ & Lain sleeping~~~ | |
console.log(lain) // Lain {name: 'lain', age: 16} | |
Lain.sleepingStatic() // Characters sleepingStatic~ & sleepingStatic~~~ | |
// console.log(Object.getOwnPropertyDescriptors(lain.__proto__)) | |
// console.log(Object.getOwnPropertyDescriptors(lain.__proto__.__proto__)) |
# class 继承内置
class _Array extends Array{ | |
arrLength() { | |
return this.length | |
} | |
} | |
const arr = new _Array(1, 2, 3) | |
arr.arrLength() // 3 |
# class 实现混入
- JavaScript 的类只支持单继承:也就是只能有一个父类
- 那么在开发中我们我们需要在一个类中添加更多相似的功能时,应该如何来做呢?
- 这个时候我们可以使用混入(mixin);
class Characters { | |
} | |
// 在 JS 中类只能有一个父类:单继承 | |
class Lain extends Characters { | |
} | |
function mixinCharacters(BaseClass) { | |
return class extends BaseClass { | |
sleeping() { | |
console.log("sleeping~") | |
} | |
} | |
} | |
const NewLain = mixinCharacters(Lain) | |
const lain = new NewLain() | |
lain.sleeping() |
# JavaScript 多态
-
传统的面向对象多态是有三个前提:
-
必须有继承 (是多态的前提)
-
必须有重写 (子类重写父类的方法)
-
必须有父类引用指向子类对象
# TS 多态
class Characters { | |
getFriends() { | |
return '伊莉雅' | |
} | |
} | |
class Lain extends Characters { | |
getFriends() { | |
return '樱岛麻衣' | |
} | |
} | |
class Saber extends Characters { | |
getFriends() { | |
return '小鸟游六花' | |
} | |
} | |
var lain = new Lain() | |
var saber = new Saber() | |
// 多态:当对不同的数据类型执行同一个操作时,如果表现出来的行为 (形态) 不一样,那么就是多态的体现. | |
function getFriends(Characters: Characters) { | |
console.log(Characters.getFriends()) | |
} | |
getFriends(lain) | |
getFriends(saber) | |
export { } |
# JS 多态
-
JavaScript 有多态吗?
-
维基百科对多态的定义:多态(英语:polymorphism)指为不同数据类型的实体提供统一的接口,或使用一个单一的符号来表示多个不同的类型。
-
非常的抽象,个人的总结:不同的数据类型进行同一个操作,表现出不同的行为,就是多态的体现。
-
-
从下面的定义来看,JavaScript 是一定存在多态的
// 多态:当对不同的数据类型执行同一个操作时,如果表现出来的行为 (形态) 不一样,那么就是多态的体现. | |
function Characters(foo) { | |
console.log(foo.getFriends()) | |
} | |
var lain = { | |
name: "lain", | |
getFriends: function() { | |
return '樱岛麻衣' | |
} | |
} | |
class Saber { | |
getFriends() { | |
return '小鸟游六花' | |
} | |
} | |
var saber = new Saber() | |
Characters(lain) // 樱岛麻衣 | |
Characters(saber) // 小鸟游六花 | |
// 也是多态的体现 | |
function sum(m, n) { | |
return m + n | |
} | |
sum(22, 33) | |
sum("樱岛麻衣", "小鸟游六花") |