学习JS设计模式总结(1)—— 理解面向对象

446 阅读5分钟

前言

虽然JS是单线程的,但是我们学习可不能单线程。利用学习Nodejs的边边角角之余,我看了些关于JS设计模式的课程,感觉是值得总结与分享的,一来可以提高自己对JS编程的认知,二来可以在实际项目中设计出简单而优雅的设计方案。 因为这个课程看的比较慢,准备分三到四篇写完,分别为面向对象编程、五大设计原则、二十三种设计模式。

何为面向对象编程

最早接触这个词语也是在学C语言的时候,那时候老师说C是一个基于面向过程的编程语言,与之相对应的就是面向对象。所谓对象,通俗来说就是某一个事物所有属性和方法的集合。比如一个人的属性就有姓名、年龄、身高、体重等等,方法则是吃饭、睡觉、说话等。下面这个例子就是简单的写了个人的构造函数People,它的实例person1 就是一个 对象。它包括人的属性和方法,对象的本质其实就是一个结构化设计的方法,把相关的属性和方法组织为一个整体来看待,从更高的层次来进行系统建模,更贴近事物的自然运行模式。

class People {
    constructor(name, age) {
        this.name = name // 姓名
        this.age = age // 年龄姓名
    }
    // 方法
    say() {
        console.log(`hello, my name is ${this.name}, I m ${this.age} years old`)
    } 
}

const person1 = new People('yellowbird', 25)
person1.say() // 打印hello, my name is yellowbird, I m 25 years old

面向对象编程的三要素

  • 继承

继承性在JS中是相当重要的存在,所谓继承就是定义的子类也同时拥有父类的属性和方法,它最大的有点是可以抽离公共方法,减少代码冗余。还是举个例子,比如学生是人类组成的其中一个子类,他可以拥有人类所有的属性和方法,如姓名年龄,吃饭睡觉他都有,但他也有他独有的属性例如学校,学号等。写个例子简单感受一下继承。

class Student extends People {
    constructor(name, age, grade) {
        super(name, age)
        this.grade = grade
    }
    study() {
        console.log(`my name is ${this.name}, my grade is ${this.grade}`)
    }
}

const student = new Student('yellowbird', 7, 'one')
student.study() //my name is yellowbird, my grade is one   
student.say() // hello, my name is yellowbird, I m 7 years old
  • 封装

所谓封装,就是隐藏方法和属性,只对外暴露公开接口。总所周知,JS是一门弱类型编程语言,所以其实封装这一概念并没有在JS中引入。而在Typescript中是有引入的。简单了解一下就ok了,为日后用到做好准备! 封装有3个关键字,分别为public(公有,不写为默认值),private(私有),protected(受保护的)。 我通过把上面继承的例子用TS改写的同时,来介绍一下它们的特点。

1、public

首先是public,它声明在变量之前,代表它可以在任何情况下被访问,不写通常默认为public。

class People {
    public name: string;
    public age: number;
    public constructor(name , age) {
        this.name = name; // 姓名
        this.age = age; // 年龄姓名
    }
    // 方法
    public say() {
        console.log(`hello, my name is ${this.name}, I m ${this.age} years old`);
    } 
}

const person1 = new People('yellowbird', 25);
person1.say(); // 打印hello, my name is yellowbird, I m 25 years old,

2、private

它在被声明之后,无法在外部访问该变量,举个例子,人们并不想把自己的身份证号被人随意获取。所以就有如下代码

class People {
    public name: string;
    public age: number;
    private id: string;
    public constructor(name, age, id) {
        this.name = name; // 姓名
        this.age = age; // 年龄姓名
        this.id = id;
    }
    // 方法
    public say() {
        console.log(`hello, my name is ${this.name}, I m ${this.age} years old`);
    } 
}

const person1 = new People('yellowbird', 25, '334111202008010129');
console.log('person1.id'); '// 私有变量无法被直接访问,报错

3、protected

它和private很相似,但是区别在于它的子类是可以访问被protected声明的属性。比如,我们不希望自己的体重被人直接获取到,但是作为学生去体检的时候会获取到体重值。

class People {
    public name: string;
    public age: number;
    protected weight: number;
    public constructor(name, age, weight) {
        this.name = name; // 姓名
        this.age = age; // 年龄姓名
        this.weight = weight;
    }
    // 方法
    public say() {
        console.log(`hello, my name is ${this.name}, I m ${this.age} years old`);
    } 
}
class Student extends People {
    constructor(name, age, weight) {
        super(name, age, weight);
    }
    getWeight() {
        console.log(`my weight is ${this.weight} kg`);
    }
}

const student = new Student('yellowbird', 25, 100);
student.getWeight(); // 打印my weight is 100 kg
console.log(student.weight); // 报错

另外,构造函数constructor也可以被声明为protected,被声明之后,该构造函数无法实例化,但可以被继承。

class People {
    public name: string;
    public age: number;
    protected weight: number;
    protected constructor(name, age, weight) {
        this.name = name; // 姓名
        this.age = age; // 年龄姓名
        this.weight = weight;
    }
    // 方法
    public say() {
        console.log(`hello, my name is ${this.name}, I m ${this.age} years old`);
    } 
}
class Student extends People {
    constructor(name, age, weight) {
        super(name, age, weight);
    }
    getWeight() {
        console.log(`my weight is ${this.weight} kg`);
    }
}

const student = new Student('yellowbird', 25, 100); // 可以继承
const person1 = new People('HP', 25, 200); // 报错, People不可以实例化

以上就是封装的解析和示例,大家细品,再结合手打代码,对封装一定会有进一步的理解。封装的作用主要是减少代码耦合,利于数据和接口的权限管理,如果你单纯用JS进行开发可能不太用得到,但是还是需要掌握的。

  • 多态

所谓多态,就是用不同方式实现同一个接口。多态在JS使用就更少了,只能模拟一下,实际开发感觉真的不好用。思路其实就是用A和B分别去继承父类,然后重写不同的方法,如下:

class People {
   constructor(name, age, weight) {
        this.name = name; // 姓名
        this.age = age; // 年龄姓名
    }
    // 方法
   say() {
        console.log(`hello, my name is ${this.name}, I m ${this.age} years old`);
    } 
}

class A extends People {
    constructor(name, age) {
        super(name, age,);
    }
    say() {
        console.log('say A')
    }
}
class B extends People {
    constructor(name, age) {
        super(name, age);
    }
    say() {
        console.log('say B')
    }
}
const a = new('a' ,20);
const b = new('b', 20);
a.say()  // say A
b.say() // say B

总结一波

理解面向对象是这个JS设计模式系统讲解与应用的前置章节,可以说是为后面的二十三种设计模式铺路的。以前总说面向对象,但其实对这个概念并不是非常的理解,不去看,真的可能说不出它的三要素是什么,以及如何实现。获益良多,希望我的总结也能帮到大家~

参考资料:

Typescript中文网

面向对象

Javascript设计模式系统讲解与应用