福利!给前端介绍对象啦!(TypeScript版)

Posted Think体验设计

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了福利!给前端介绍对象啦!(TypeScript版)相关的知识,希望对你有一定的参考价值。


 引言:面向对象好 



 类与对象:一切皆对象 


回到一个古老的话题:程序是什么?

程序=数据结构+算法

在面向对象编程(Object Oriented Programming, OOP)世界里,一切皆对象。

面向对象程序=以对象为最小单位的数据结构+基于对象的操作算法

类表示一类事物,而对象表示一个具体的事物。例如:学生是类,而李雷和韩梅梅都是对象。

(温馨提示:左右滑动可查看全部代码)

class Student {//学生类
       constructor(public name:string){
       }
}
let li: Student = new Student("Li Lei");// 李雷对象
let han: Student = new Student("Han Meimei");//韩梅梅对象 


 OOP 三大特征 


封装:你办事,我放心。我不知道你内部如何运作。


类是属性和方法的集合,封装就是对外暴露可操作的属性和方法,隐藏其他实现细节。属性描述同一类事物的特征,方法描述同一类事物的操作。例如:下面Animal类,对外暴露可读写的name/age/totalNum成员属性和可调用的eat()/sleep()/say()成员方法。后面还有一些例子中private属性和方法,是不对外暴露的隐藏属性和方法。


抽象类含有未实现的抽象方法,等待子类去实现,所以不能实例化。例如:抽象类Animal含有未实现的抽象方法say(),。


静态属性和静态方法是类所有,并不是每个对象所有,是全局性的属性和方法。例如:静态成员变量totalNum是类所有,记录已实例化动物总数。

(温馨提示:左右滑动可查看全部代码)

//抽象类
abstract class Animal {
       public static totalNum = 0;   //静态成员
       constructor(public name: string, public age: number) {//转化为成员变量语法糖
               Animal.totalNum++;
       };
       public eat() {
               console.log("I am eating someting");
       }
       public sleep() {
               console.log("I am sleeping");
       }
       public abstract say();
}


测试用例:

(温馨提示:左右滑动可查看全部代码)

// let animal: Animal = new Animal("Error",10);//编译报错:无法创建抽象类。



继承:龙生龙,凤生凤,老鼠的儿子会打洞 


子类继承了父类所有的属性和方法,并可以拥有自己新的属性和方法。继承解决了代码重用的问题。例如:Dog类继承了Animal类,拥有了Animal类所有的属性和方法name//age/sleep()/eat() /say()。并且可以覆盖和扩展,eat() /say()覆盖了父类的方法实现, wagtail()是扩展出父类没有的新方法。


private/protected/public关键字,限定了对外可见范围。private只能在类内部访问;protected只能在类和子类内部访问;默认public,在类内部外部都能访问。

(温馨提示:左右滑动可查看全部代码)

class Dog extends Animal {
       private shoutNum: number = 3;
       public eat() {//多态 覆盖原方法
               console.log("I am eating meat");
       }
       private getShoutSting(): string{
               let shoutString: string='';
               for(let i=0;i<this.shoutNum;i++){
                       shoutString+="Wong!";
               }
               return shoutString;
       }
       public say() {
               console.log(this.getShoutSting());
       }
       public wagTail() {//扩展新方法
               //...
       }
}


测试用例:

(温馨提示:左右滑动可查看全部代码)

let myDog: Animal = new Dog("DaHuang", 5);
      myDog.sleep();//I am sleeping
      myDog.eat();//I am eating meat
      myDog.say(); //Wong!Wong!Wong!
      // myDog.getShoutSting();//编译错误


 多态:我就是我,不一样的烟火 


多态是指同一个方法在子类中具有不同表现形式。例如:Dog类和Cat类都有eat ()/say ()方法,尽管都是来自Animal定义的eat ()/say ()方法,但运行结果完全不同。

(温馨提示:左右滑动可查看全部代码)

class Cat extends Animal {
       public eat() {//多态 扩展原方法
               super.eat();
               console.log("I like eating fish");
       }
       public say() {
               console.log("Miao~");
       }
       public catchMouse() {
               //...
       }
}


测试用例:

let myCat: Animal = new Cat("XiaoHua", 3);
       myCat.eat();//I am eating someting     I like eating fish
       myCat.say();//Miao~


 接口 


接口:没有规矩不成方圆


接口即规范,规定了某种能力必须要实现的方法。例如:Flyable接口规定了具有飞行能力必须实现fly()方法。Bird和Plane类都实现了Flyable接口,具有飞行能力。

(温馨提示:左右滑动可查看全部代码)

interface Flyable {
       fly();
}
interface catchBug {
       catchBug();
}
class Bird extends Animal implements Flyable,catchBug {
       public say() {
               console.log("Ji~Ji~Zha~Zha~");
       }
       public fly() {
               console.log("I am flying with flapping my wings");
       }
       public catchBug() {
               //...
       }
}
class Plane implements Flyable {
       fly() {
               console.log("I am flying with my engines");
       }
       takePeople() {
               //...
       }
}


测试用例:

(温馨提示:左右滑动可查看全部代码)

let flyable1: Flyable = new Bird("Polly", 1);
       flyable1.fly(); //I am flying with flapping my wings
       // flyable1.eat();//编译报错: 不存在eat()

       let flyable2: Flyable = new Plane();
       flyable2.fly();//I am flying with my engines


抽象类与接口:有同有异


抽象类和接口很类似,都有未实现的方法,等待其他类去实现。区别是:抽象类可以有一些成员的实现,而接口没有成员的实现;一个类只能继承自一个抽象类,但可以实现多个接口。例如:Animal类,有抽象方法say(),也有已实现的方法;上面Bird类只能继承自一个类,但又同时实现了flyable()/catchBug()两个接口。


 OOP 设计原则 


李氏替换原则:父债可以子来偿


所有引用基类的地方必须能透明的使用其子类的对象,因为子类的对象具有基类所有的属性和方法。(注:基类是指父类/父类的父类等)但反过来就不行了,有需要子类的地方,基类未必就能适应。例如:上面代码中,我们可以将Dog对象和Cat对象赋值给了Animal类型的变量。下面代码中,People构造函数需要一个Animal类型的参数,我们传入了Dog类型的对象。

(温馨提示:左右滑动可查看全部代码)

class People extends Animal{
       constructor(public name: string, public age: number,public pet: Animal) {
               super(name,age);
       };
       public say(){
               console.log(`My name is ${this.name}, I am ${this.age} years old, my pet's name is ${this.pet.name}`);
       }
}


测试用例:

(温馨提示:左右滑动可查看全部代码)

let myBrother: People = new People("XiaoMing", 18, new Dog("GouDan", 2));
      myBrother.say();//My name is XiaoMing, I am 12 years old, my pet's name is GouDan
      console.log('totalNum='+Animal.totalNum);//totalNum=5 //Dog/Cat/Bird/Dog/People/

 

依赖倒置原则:面向接口编程,大家都依赖接口


在无接口的代码组织中,是高层模块依赖低层模块。如果按照面向对象编程依赖倒置原则,高层模块和低层模块不互相依赖,他们都依赖于其接口/抽象类。例如:如果不存在接口,直接调用Bird类/Plane类的fly()方法,那么调用者便依赖于Bird类/Plane类。在上面例子中采用了接口编程,调用者的调用依赖于Flyable()接口;Bird类和Plane类的实现都依赖于Flyable()接口。


请看下图,原本高层模块向下依赖的箭头,变为低层模块向上依赖的箭头。这就是依赖倒置名称的由来。


无接口:

              

有接口,依赖倒置:

福利!给前端介绍对象啦!(TypeScript版)


其他设计原则


单一职责、开闭原则、接口隔离、迪米特法则等其他OOP设计原则呢,后会有期。


 总结 


在一切皆对象的世界中,我们已经学习了面向对象三大特征——封装、继承、多态,和两条面向对象设计原则——李氏替换原则、依赖倒置原则。后面有机会再介绍其他OOP设计原则和GoF设计模式。


 

— END —


福利!给前端介绍对象啦!(TypeScript版)


福利!给前端介绍对象啦!(TypeScript版)有什么想对作者说的吗?快来留言吧!福利!给前端介绍对象啦!(TypeScript版)


相关链接:





THINK体验设计

微信ID:hw-Think

长按二维码关注有惊喜

福利!给前端介绍对象啦!(TypeScript版)




点击“阅读原文”,进入华为云官网 ! 

以上是关于福利!给前端介绍对象啦!(TypeScript版)的主要内容,如果未能解决你的问题,请参考以下文章

福利 | 酷客多小程序商家版上线啦

图解23种设计模式(TypeScript版)——前端必修内功心法

1W字图解23种设计模式(TypeScript版)——前端切图崽必修内功心法

沪江Web前端技术团队倾情翻译JavaScript 轻量级函数式编程登场啦(结尾有福利!)

实现一个 async/await (typescript 版)

通俗易懂的 TypeScript 入门教程