1、要想面向对象,操作对象,首先便要拥有对象;
2、要创建对象,必须要先定义类,所谓的类可以理解为对象的模型;
3、程序中可以根据类创建指定类型的对象;
举例来说:
- 操作浏览器要使用window对象
- 操作网页要使用document对象
- 操作控制台要使用console对象
一、定义类
class 类名 {
属性名: 类型 = 值;
方法名(){
....
}
}
示例:
class Person{
//定义实例属性,需要通过对象实例访问:
/*
const per = new Person();
per.name;
*/
name: string = "张三";
//使用static开头的属性是静态属性(类属性),可以直接通过类访问
/*
Person.age;
*/
static age: number = 18;
//使用readonly开头的属性表示一个只读的属性无法修改
readonly color:string = "blue"
//顺序不可颠倒
readonly static address:string = "shanxi"
sayHello(){
console.log('hello');
}
}
const p = new Person();
p.name;
Person.age
p.sayHello();
二、构造函数
可以使用constructor
定义一个构造器方法;
例如:
class Dog {
name:string;
age:number;
constructor(name: string, age: number) {
//this表示当前实例
//可以通过this向新建的对象中添加属性
this.name = name;
this.age = age;
}
bark(){
//在方法中可以通过this来表示当前调用方法的对象
consloe.log(this.name)
}
}
const dog = new Dog('小黑',2)
const dog2 = new Dog('小白',3)
dog.bark()
三、继承
继承时面向对象中的又一个特性
通过继承可以将其他类中的属性和方法引入到当前类中
示例:
class Animal {
name:string;
age: number;
constructor(name:string,age:number){
this.name = name;
this.age = age;
}
sayHello(){
console.log('叫声');
}
}
/*
Dog extends Animal
- 此时,Animal被称为父类,Dog被称为子类
- 使用继承后,子类将会拥有父类所有的方法和属性
- 通过继承可以将多个类共有的代码写在一个父类中
- 如果希望在子类中添加父类中没有的属性或方法,直接加即可
- 如果在子类中添加了和父类相同的方法,则子类方法会覆盖掉父类的方法,这种形式,我们称之为重写
*/
class Dog extends Animal{
run(){
console.log('汪汪')
}
}
class Cat extends Animal{
sayHello(){
console.log('喵喵');
}
}
const dog = new Dog('旺财',2)
const cat = new Cat('招财',3)
console.log(dog);
console.log(cat);
通过继承可以在不修改类的情况下完成对类的扩展
四、重写
发生继承时,如果子类中的方法会替换掉父类中的同名方法,这就称为方法的重写
示例:
class Animal {
name:string;
age: number;
constructor(name:string,age:number){
this.name = name;
this.age = age;
}
sayHello(){
console.log('叫声');
}
}
class Cat extends Animal{
sayHello(){
console.log('喵喵');
}
}
五、super
在子类中可以使用super 来完成对父类的引用
class Animal {
name: string
constructor(name:string){
this.name = name
}
sayHello(){
console.log('123');
}
}
class Dog extends Animal {
sayHello(){
//super表示当前类的父类
super.sayHello();
}
}
const dog = new Dog('wangcai')
dog.sayHello() //123
子类继承父类时,必须调用父类的构造方法(如果子类中也定义了构造方法)!
例如:
class Animal {
name: string
constructor(name:string){
this.name = name
}
sayHello(){
console.log('123');
}
}
class Dog extends Animal {
age:number;
constructor(name:string,age:number){
super(name); //调用父类的构造函数
this.age = age
}
sayHello(){
//super表示当前类的父类
super.sayHello();
}
}
const dog = new Dog('wangcai',4)
dog.sayHello() //123
如果在子类中不调用
super
将会报错!
六、抽象类(abstract class)
-
以abstract开头的类是抽象类
-
抽象类和其他类区别不大,但不能用来创建对象
-
抽象类是专门用来被其他类所继承的类,它只能被其他类所继承不能用来创建实例
-
使用abstract开头的方法叫做抽象方法,抽象方法没有方法体,只能定义在抽象类中,子类必须对抽象方法进行重写
abstract class Animal {
name: string
constructor(name:string){
this.name = name
}
//抽象方法
abstract sayHello():void
}
class Dog extends Animal {
sayHello(){
console.log('汪汪');
}
}
const dog = new Dog('wangcai')
dog.sayHello() //汪汪
七、接口(Interface)
- 接口用来定义一个类结构,用来定义一个类中应该包含哪些属性和方法,同时也可以当成类型声明去使用
interface myInterface {
name:string,
age:number
}
interface myInterface {
gender:string,
}
const obj:myInterface = {
name:'张三',
age:18,
gender: '男'
}
- 接口主要负责定义一个类的结构,接口可以去限制一个对象的接口:对象只有包含接口中定义的所有属性和方法时才能匹配接口;
- 接口的作用类似于抽象类,不同点在于:接口中的所有方法和属性都是没有实值的,换句话说接口中的所有方法都是抽象方法
interface myInter {
name:string
sayHello():void
}
- 同时,可以让一个类去实现接口,实现接口时类中要保护接口中的所有属性
interface myInter {
name:string
sayHello():void
}
class MyClass implements myInter {
name:string;
constructor(name:string){
this.name = name
}
sayHello(){
console.log('hello');
}
}
八、属性的封装
对象实质上就是属性和方法的容器,它的主要作用就是存储属性和方法,这就是所谓的封装
默认情况下,对象的属性是可以任意的修改的,为了确保数据的安全性,在TS中可以对属性的权限进行设置
-
静态属性(static)
声明为static的属性或方法不再属于实例,而是属于类的属性;
-
只读属性(readonly)
如果在声明属性时添加一个readonly,则属性便成了只读属性无法修改
-
TS中属性具有三种修饰符
- public(默认值),修饰的属性可以在任意位置访问(修改)
- protected ,可以在当前类和当前类的子类中访问(修改)
- private ,私有属性,只能在类内部进行访问(修改),可通过在类中添加方法使得私有属性可以被外部方法访问
优势:掌控属性控制权,使代码更健壮,降低代码出错几率
示例:
public:
class Person{
public name: string; // 写或什么都不写都是public
public age: number;
constructor(name: string, age: number){
this.name = name; // 可以在类中修改
this.age = age;
}
sayHello(){
console.log(`大家好,我是${this.name}`);
}
}
class Employee extends Person{
constructor(name: string, age: number){
super(name, age);
this.name = name; //子类中可以修改
}
}
const p = new Person('张三', 18);
p.name = '李四';// 可以通过对象修改
同时也可以直接将属性定义在构造函数中(语法糖):
class Person {
constructor(public name: string, public age: number) {
}
}
protected:
class C {
protected num:number;
constructor(num:number){
this.num = num
}
}
class D extends C {
test(){
console.log(this.num);
}
}
const d = new D(18)
console.log(d.test()); //18
console.log(d.num); //报错,无法访问
d.num = 20 //报错,无法修改
private:
class Person {
private name: string;
private age:number;
constructor(name:string,age:number){
this.name = name;
this.age = age;
}
//getter方法用来读取属性
//setter方法用来设置属性
//定义方法,用来获取name属性
getName(){
return this.name;
}
//定义方法,用来设置name
setName(val:string){
this.name = val;
}
//定义方法,用来获取age
getAge(){
return this.age
}
//定义方法,用来设置name
setAge(val:number){
if(val >= 20){
this.age = val
}
}
}
const per = new Person('张三',18);
console.log(per.name); //报错,无法访问
console.log(per.age); //报错,无法访问
console.log(per.getName()); //张三
per.setName('李四');
console.log(per.getName()); //李四
console.log(per.getAge()); //18
per.setAge(19) // <20
console.log(per.getAge()); //18
per.setAge(30) // >20
console.log(per.getAge()); //30
class A {
private num:number;
constructor(num:number){
this.num = num
}
}
class B extends A {
test(){
console.log(this.num); //报错,不能访问
}
}
九、属性存取器
- 对于一些不希望被任意修改的属性,可以将其设置为private
- 直接将其设置为private将导致无法再通过对象修改其中的属性
- 我们可以在类中定义一组读取、设置属性的方法,这种对属性读取或设置的属性被称为属性的存取器
- 读取属性的方法叫做setter方法,设置属性的方法叫做getter方法
示例:
class Person {
private _name: string;
private _age:number;
constructor(name:string,age:number){
this._name = name;
this._age = age;
}
//TS中设置getter方法的方法
get name(){
return this._name;
}
//TS中设置setter方法的方法
set name(val:string){
this._name = val;
}
get age(){
return this._age;
}
set age(val:number){
if(val >= 20){
this._age = val
}
}
}
const per = new Person('张三',18);
console.log(per.name); //张三
per.name = "李四"
console.log(per.name); //李四
console.log(per.age); //18
per.age = 30
console.log(per.age); //30