ES5中本无类这一概念, 多是利用函数所具有的局部作用域进行类的封装
而ES6与TS中提出了类这一概念
ES5的类
声明
属性和方法可以直接在构造函数或者原型链中声明, 这样声明的方法为实例方法
二者区别是原型链声明的方法与属性是多个实例所共享的, 而构造函数生成的方法是各实例独占的,互不影响的
function Person() {
this.name = '张三';
this.age = '20';
this.run = function() { console.log(`${this.name}runing`); }; // 实例方法(构造函数)
}
Person.prototype.sex = 'man';
Person.prototype.work = function() { console.log(`${this.name}working`); };// 实例方法(原型链)
const p1 = new Person();
const p2 = new Person();
console.log(p1.run === p2.run); // false
console.log(p1.work === p2.work); // true
继承
ES5通过 原型链或者对象冒充 的方式实现继承
function Person() {
this.name = '张三';
this.age = '20';
this.run = function() {
console.log(`${this.name}runing`);
}; // 实例方法(构造函数)
}
Person.prototype.sex = 'man';
Person.prototype.work = function() {
console.log(`${this.name}working`);
}; // 实例方法(原型链)
// 对象冒充实现继承
// 对象冒充只能继承构造函数中属性与方法,但是原型链无法继承
function Man() {
Person.call(this);
}
const man = new Man();
console.log(man); // Man {name: "张三", age: "20", run: ƒ} 但是prototype种没有对应的work函数
man.run(); // 张三runing
// man.work(); // 报错
// 原型链实现继承
// 原型链实现继承,对构造函数和原型链方法都可以进行继承
function Worman() {}
Worman.prototype = new Person();
const worman = new Worman();
console.log(worman); // { __proto__: { name: "张三", age: "20", run: ƒ, __proto__: {sex: "man", work: f }}}
worman.run(); // 张三runing
worman.work(); // 张三working (如果函数改用箭头函数, 则this指向widow.从而返回是working))
看似原型链继承就足够用了,但是在构造函数的属性需要赋值时, 原型链就会有问题出现,
function Person(name, age) {
this.name = name;
this.age = age;
this.run = function() {
console.log(`${this.name}runing`);
}; // 实例方法(构造函数)
}
Person.prototype.sex = 'man';
Person.prototype.work = function() {
console.log(`${this.name}working`);
}; // 实例方法(原型链)
// 对象冒充实现继承
// 对象冒充可以给子类赋值
function Man(name, age) {
Person.call(this, name, age);
}
const man = new Man('李华', 18);
console.log(man); // Man {name: "李华", age: 18, run: ƒ}
man.run(); // 李华runing
// man.work(); // 报错
// 原型链实现继承
// 原型链可以获取原型链上方法和属性
function Worman(name, age) {}
Worman.prototype = new Person();
const worman = new Worman('王梅', 20); // 子类的实例化无法给父类传参
console.log(worman); // { __proto__: { name: undefined, age: undefined, run: ƒ, __proto__: {sex: "man", work: f }}}
worman.run(); // undefinedruning
worman.work(); // undefinedworking
由此得出, 对象冒充可以给子类赋值同时原型链可以获取原型链上方法和属性, 此时,便可以使用原型链 + 对象冒充一起实现继承
function Person(name, age) {
this.name = name;
this.age = age;
this.run = function() {
console.log(`${this.name}runing`);
}; // 实例方法(构造函数)
}
Person.prototype.sex = 'man';
Person.prototype.work = function() {
console.log(`${this.name}working`);
}; // 实例方法(原型链)
// 原型链实现继承
function Worman(name, age) { Person.call(this, name, age); }
Worman.prototype = new Person(); // Worman.prototype = Person.prototype 也行
const worman = new Worman('王梅', 20); // 子类的实例化无法给父类传参
console.log(worman); // { __proto__: { name: '王梅', age: 20, run: ƒ, __proto__: {sex: "man", work: f }}}
worman.run(); // undefinedruning
worman.work(); // undefinedworking
静态
也可以直接对类进行方法添加, 这种方法是静态方法
function Person() {
this.zname = '张三'; // 实例属性
this.run = function() {
console.log(`${this.zname} + ${this.wname}runing`);
}; // 实例方法
}
Person.wname = '王五'; // 静态属性
Person.work = function() {
console.log(`${this.zname} + ${this.wname} working`);
}; // 静态方法
const p = new Person();
p.run(); // 张三 + undefinedruning
// p.work(); // 报错
// Person.run(); // 报错
Person.work(); // undefined + 王五 working
TS中的类
声明
class Person {
name: string; // 属性
constructor(n: string) {
// 构造函数,实例化触发方法
this.name = n;
}
getName(): string {
console.log(this.name);
return this.name;
}
setName(name: string): void {
this.name = name;
}
}
var p = new Person('张三');
p.getName(); // 张三
继承
TS实现继承主要使用extends与super实现
class Person {
name: string;
constructor(n: string) {
this.name = n;
}
getName(): string {
console.log(this.name);
return this.name;
}
setName(name: string): void {
this.name = name;
}
}
// 使用extends继承
class Man extends Person {
constructor(name: string) {
super(name); // super,初始化父类构造函数
}
work(): void {
console.log(this.name + 'working');
}
}
const man = new Man('李四')
man.getName() // 李四
man.work() // 李四working
但是父子类之间若方法重名,则优先执行子类方法
class Person {
name: string;
constructor(n: string) {
this.name = n;
}
getName(): string {
console.log(this.name);
return this.name;
}
setName(name: string): void {
this.name = name;
}
}
// 使用extends继承
class Man extends Person {
constructor(name: string) {
super(name); // super,初始化父类构造函数
}
getName(): string {
console.log(this.name + '在子类');
return this.name;
}
}
const man = new Man('李四');
man.getName(); // 李四在子类 (但是函数的形式还是需要按照父类的同名函数的形式构建)
静态
静态属性只能静态方法使用,同时静态方法只有类可使用
class Person {
public name: string;
static age: number = 20; // 静态属性
constructor(name) {
this.name = name;
}
// 实例方法
run() {
console.log(this.name + 'run');
// console.log(this.age + 'run'); // 报错,因为静态属性只能在静态方法中使用
}
// 静态方法
static work() {
// console.log(this.name+'work') // 报错,因为静态方法只能用静态属性
console.log('work' + this.age);
}
}
var p = new Person('李华');
// 实例方法调用
p.run();
// Person.run(); // 报错
// 静态方法调用
Person.work();
// p.work(); // 报错
权限
public
公共,在类里面,子类,类外面都可以访问,同时也是默认值
class Person {
public name: string
constructor(n: string) {
this.name = n
}
getName(): void {
console.log(this.name)
}
}
class Man extends Person {
constructor(name: string) {
super(name);
}
run(): void {
console.log(this.name)
}
}
var p = new Person('张三')
var m = new Man('李四')
// 内部访问
p.getName() // 张三
// 外部访问
console.log(p.name) // 张三
// 子类访问
m.run() // 李四
protected
保护, 在类与子类可以访问,但类外部无法访问
class Person {
protected name: string
constructor(n: string) {
this.name = n
}
alertName(): void {
console.log(this.name)
}
}
class Man extends Person {
constructor(name: string) {
super(name);
}
run(): void {
console.log(this.name)
}
}
var p = new Person('张三')
var m = new Man('李四')
// 内部访问
p.alertName() // 张三
// 外部访问
// console.log(p.name) // 属性“name”受保护,只能在类“Person”及其子类中访问
// 子类访问
m.run() // 李四
private
私有,只有类可以访问,子类和外部不可访问
class Person {
private name: string
constructor(n: string) {
this.name = n
}
alertName(): void {
console.log(this.name)
}
}
class Man extends Person {
constructor(name: string) {
super(name);
}
run(): void {
console.log(this.name)
}
}
var p = new Person('张三')
var m = new Man('李四')
// 内部访问
p.alertName() // 张三
// 外部访问
// p.name // 属性“name”为私有属性,只能在类“Person”中访问。
// 子类访问
// m.run() // 属性“name”为私有属性,只能在类“Person”中访问。
只读
使用readyonly添加只读属性
class Person {
readonly name: string
constructor(n: string) {
this.name = n
}
alertName(): void {
console.log(this.name)
}
}
var p = new Person('张三')
// p.name = '李四' // 无法分配到 "name" ,因为它是只读属性。
多态
父类方法父类不实现,尤其子类实现,且子类实现形式各不同
也是继承的一种表现
class Animal {
name: string
constructor(name: string) {
this.name = name
}
// 定义方法由子类实现
eat() {
console.log('吃的方法')
}
}
class Dog extends Animal {
constructor(name: string) {
super(name)
}
eat() {
return this.name + '吃骨头'
}
}
class Cat extends Animal {
constructor(name: string) {
super(name)
}
eat() {
return this.name + '吃鱼'
}
}
抽象
使用abstract关键子定义抽象类与方法
抽象类提供其他类继承的基类, 并不能直接被实例化.用于定义标准
抽象类的抽象方法必须在派生类中实现
不同于接口,抽象类可以包含成员的实现细节
抽象方法只能在抽象类中
抽象亦是中多态
abstract class Animal {
public name: string;
constructor(name: string) {
this.name = name
}
abstract eat(): any;
}
// var a=new Animal() // 无法创建抽象类的实例
class Dog extends Animal {
// >>>>> 这里忘记写name, 标记下
constructor(name) {
super(name)
}
// eat 不声明会报错
eat() {
return this.name + '吃骨头'
}
}
今天(9月17号)看项目时发现抽象类的一种用法,在此更新下心得
abstract class Animal {
public name: string;
constructor(name: string) {
this.name = name
}
abstract eat(): any;
see(){
console.log('see');
}
}
// var a=new Animal() // 无法创建抽象类的实例
class Dog extends Animal {
constructor(name) {
super(name)
}
// eat 不声明会报错
eat() {
return this.name + '吃骨头'
}
see(){
super.see() // 此时则会执行Animal中see的方法
console.log(this.name + 'see')
}
}
const dog = new Dog('dog')
dog.see() // see dogsee
这样可以把抽象类当作一种类别最基础的实现, 继承该抽象类的类可以再某些方法使用super执行抽象类中的方法, 并可以继续执行在继承类中相同方法的剩余代码
abstract class Animal {
public name: string;
constructor(name: string) {
this.name = name
}
abstract eat(): any;
see(){
console.log('see');
}
}
// var a=new Animal() // 无法创建抽象类的实例
class Dog extends Animal {
constructor(name) {
super(name)
}
// eat 不声明会报错
eat() {
return this.name + '吃骨头'
}
see(){
console.log(this.name + 'see')
}
}
const dog = new Dog('dog')
dog.see() // dogsee
但是super.see(), 则继承类中的see等同于重写了抽象类中的see