前端 JavaScript 设计模式前奏--面向对象-封装继承多态
Posted 黑木令
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端 JavaScript 设计模式前奏--面向对象-封装继承多态相关的知识,希望对你有一定的参考价值。
javascript 面向对象
如图所示:
1. 什么是面向对象
1. 面向对象的概念
2. 面向对象的三要素: 继承/封装/多态 ⭐️
3. JS 的应用举例
4. 面相对象的意义 --- 数据结构化
2. 面向对象 的概念: 类 + 对象
1. 面向对象 的概念: 类 + 对象
1. 类(模板) / 对象(实例)
1. 通过 class 这个 类(模板)来实例化很多对象 (如同 ES5 的构造函数一样) 。
2.1 对象(实例):
类图关系如图所示:
1. 对象(实例):
1. 所谓对象: 本质上就是指事物 (包括人和物) 在程序设计语言中的表现形式 。 这里的事物可以是任何东西 (如某个客观存在的对象, 或者某些较为抽象的概念)。 例如, 对于猫这种常见对象来说, 具有某些明确的特征 (如 颜色/名字/体型 等), 能执行某些动作 (如 喵喵叫/睡觉/躲起来/逃跑 等)。 在 OOP 语义中, 这些对象特征都叫做属性, 而那些动作则被称为方法 。
2. 此外, 还有一个口语方面的类比: 对象往往是用名词表示的 (如 book、person), 方法一般都是些动词(如read、run), 属性值则往往是一些形容词 。
// 概念 -- 对象(实例)
class People2
constructor(name, age)
this.name = name
this.age = age
eat2()
document.write(`$this.name eat something`)
speak2()
document.write(`my name is $this.name, age $this.age`)
// 创建实例
let zhang2 = new People2('zhang', '20')
zhang2.eat2()
zhang2.speak2()
// console.log(zhang.eat2())
// console.log(zhang.speak2())
// 创建实例
let wang2 = new People2('wang', '22')
wang2.eat2()
wang2.speak2()
// console.log(wang.eat2())
// console.log(wang.speak2())
2.2 类(模板):
2. 类(模板):
1. 在现实生活中, 相似对象之间往往都有一些共同的组成特征 。 例如蜂鸟和老鹰都具有鸟类的特征, 因此它们可以被统称为鸟类 。
2. 在 OOP 中, 类实际上就是对象的设计蓝图或制作配方 。 对象这个词, 有时候也叫做实例 。 所以, 老鹰是鸟类的一个实例 。 可以基于同一个类创建出许多不同的对象, 因为类更多的是一种模板 。 而对象则是在这些模板的基础上被创建出来的实体 。
3. javascript 实际上压根没有类 。 该语言的一切都是基于对象的, 其依靠的是一套原型 (prototype) 系统 。 而原型本身实际上也是一种对象 。
4. 在传统的面向对象语言中, 基于 Person 类创建了一个 Match 的新对象; 而在 javascript 中, 则是将现有的 Person 对象扩展成一个 Match 的新对象 。
// 概念 -- 类(即 模板)
export class Person1
// 初始化时需要传入什么内容是由 constructor 这里来定义的 。
constructor(name, age)
// 特性(属性)
this.name = name
this.age = age
// 动作(方法)
eat()
document.write(`$this.name eat something`)
speak()
document.write(`my name is $this.name, age $this.age`)
3. 面向对象的三要素: 封装/继承/多态
如图所示:
1. 封装 (public:完全开放; protected:对子类开放; private:对自己开放)
1. 封装: 数据的权限和保密 (利于数据, 接口的权限管理)。
1. 封装主要用于阐述对象中所包含的内容 。 封装概念通常由两部分组成: 相关的数据 (用于存储属性), 基于这些数据所能做的事 (所能调用的方法)。
2. 减少耦合, 不该外漏的不外漏
1. 封装的目的是将信息隐藏, 即方法与属性的可见性 。 一般而言, 封装包括 封装数据和封装实现 。
3. 在许多语言的对象系统中, 封装数据是由语法解析来实现的, 这些语言提供了 public(完全开放) / private(对自己开放/私有的) / protected(对子类开放) 这些关键字来限定方法和属性的可见性, 这种限定分类定义了 对象用户 所能访问的层次 。
4. 但 javascript 并没有提供对这些关键字的支持, 只能依赖 变量的作用域 来实现封装特性, 而且只能模拟出 public 和 private 这两种封装性 。 除了 ECMAScript6 中提供的 let 之外, 一般通过函数来创建作用域 。
5. ES6 尚不支持, 一般认为 _ 开头的属性为私有属性; 可以使用 typeScript 来演示 (typeScript 是 JavaScript 的超级)
2. 继承
1. 继承: 就是子类继承父类
1. 通过继承这种方法, 可以非常优雅地实现对现有代码的重用 。 在传统的 OOP 环境中, 继承通常指的是类与类之间的关系, 但由于 javascript 中不存在类, 因此它的继承只能发生在对象之间 。
2. 比如, 有一个 Person 的一般性对象, 其中包含一些 姓名、性别 之类的属性, 以及一些功能性函数, 如 步行、谈话、睡觉、吃饭 等。 然后, 需要一个 Programmer 对象时, 可以让 Programmer 继承自 Person; Programmer 对象只需要实现属于它自己的那部分特殊功能(如编写代码), 而其余部分重用 Person 的实现即可 。
3. 当一个对象继承自另一个对象时, 通常会往其中加入新的方法, 以扩展被继承的老对象 。 通常将这一过程称之为 "B继承自A" 或 "B扩展自A" 。 另外对于新对象来说, 它可以根据自己的需要, 从继承的那组方法中选择几个来重新定义 。 这样做并不会改变对象的接口, 因为其方法名是相同的, 只不过当调用新对象时, 该方法的行为与之前不同了 。 这种重定义继承方法的过程叫做覆写 。
3. 多态
1. 同一接口, 不同实现 (不同表现); JS 应用极少 。
1. 原因: 需要结合 java 等语言的 接口/重写/重载 等功能; 这些 JS 中是没有的 。
1. 多态一词源于希腊文 polymorphism, 拆开来看是 poly(复数) + morph(形态) + ism, 从字面意思可以理解为 复数形态 。
2. 多态的实际含义是: 同一个操作作用于不同的对象上面, 可以产生不同的解释和不同的执行结果 。 换句话说, 给不同的对象发送同一个消息的时候, 这些对象会根据这个信息分别给出不同的反馈 。
3. Programmer 对象继承了上一级对象 Person 的所有方法 。 这意味着这两个对象都实现了 "talk" 等方法 。 现在, 代码中有一个叫做 “Match” 的变量, 即使是在不知道它是一个 Person 对象还是一个 Programmer 对象的情况下, 也依然可以直接调用该对象的 "talk" 方法, 而不必担心这会影响代码的正常工作 。 类似这种不同对象通过相同的方法调用来实现各自行为的能力, 称之为多态 。
4. 多态背后的思想是将 "做什么" 和 "谁去做以及怎样去做" 分离开来, 也就是将 “不变的事物” 与 “可能改变的事物” 分离开来 。 把不变的部分隔离出来, 把可变的部分封装起来, 这给予了我们扩展程序的能力, 程序看起来是可生长的, 也符合开放——封闭原则, 相对于修改代码来说, 仅仅增加代码就能完成同样的功能, 这显然优雅和安全得多 。
5. 多态最根本的作用是通过把 过程化的条件分支语句 转化为 对象的多态性, 从而消除这些条件分支语句 。
3.1 封装-代码示例
/**
1. 封装:
*/
var myObject1 = (function()
var name = 'match'; // 私有 (private) 变量
return
getName: function() // 公开 (public) 方法
return name;
)();
console.log(myObject1.getName() );// 输出: match
console.log(myObject1.name) // 输出: undefined
// // http://www.typescriptlang.org/play/ <线上转化代码网址>
// // 使用 typeScript 来演示封装效果
// class People3
// // name age 相当于是 public 声明的属性
// name
// age
// // 定义 protected 属性(可以自己获取, 也可以在子类中获取)
// protected weight
// constructor(name, age)
// this.name = name
// this.age = age
// this.weight = 120
//
// eat3()
// document.write(`$this.name::: eat something`)
//
// speak3()
// document.write(`my name is::: $this.name, age::: $this.age`)
//
//
// class Students3 extends People3
// num
// // 定义 private 属性(私有的, 只能在当前这个子类中使用, 不能通过方法调用)
// private girlFriend
// constructor(name, age, num)
// super(name, age)
// this.num = num
// this.girlFriend = '小红'
//
// study3() // 子类特有的方法
// document.write('<br>' + `$this.name study::: $this.num` + '<br>')
//
// getWeight3()
// document.write('<br>' + `$this.name的体重::: $this.weight` + '<br>')
//
//
// // 使用方法
// let xiaoming3 = new Students3('小明', 333, 'cccc')
// xiaoming3.study3()
// xiaoming3.eat3()
// xiaoming3.getWeight3()
// console.log(xiaoming3.girlFriend) // 注意: 编译时会报错, 编译直接不通过; 因为 girlFriend 是私有的, 只能在子类 Students3 中去使用 。
// 编译过之后的代码
class People3
constructor(name, age)
this.name = name;
this.age = age;
this.weight = 120;
eat3()
document.write(`$this.name::: eat something`);
speak3()
document.write(`my name is::: $this.name, age::: $this.age`);
class Students3 extends People3
constructor(name, age, num)
super(name, age);
this.num = num;
this.girlFriend = '小红';
study3()
document.write('<br>' + `$this.name study::: $this.num` + '<br>');
getWeight3()
document.write('<br>' + `$this.name的体重::: $this.weight` + '<br>');
// 使用方法
let xiaoming3 = new Students3('小明', 333, 'cccc');
xiaoming3.study3();
xiaoming3.eat3();
xiaoming3.getWeight3();
console.log(xiaoming3.girlFriend); // 注意: 编译时会报错, 编译直接不通过; 因为 girlFriend 是私有的, 只能在子类 Students3 中去使用 。
3.2 继承-代码示例
/**
2. 继承:
*
1. Person3 是父类, 公共的, 不仅仅是服务于 Students
2. 继承可以将公共方法抽离出来, 提高复用, 减少沉余
*/
// 父类
class Person3
constructor(name, age)
this.name = name
this.age = age
eat()
console.log(`$this.name: eat something`)
speak()
console.log(`my name is: $this.name, age: $this.age`)
// 子类: 子类继承父类
class Students3 extends Person3
constructor(name, age, num)
super(name, age) // super 将 name, age 传递给父类来执行 。
this.num = num
study() // 子类特有的方法
console.log(`$this.name study something: $this.num`)
// 使用方法
let xiaoming = new Students3('xiaoming', 10, 'aaa')
console.log(xiaoming.study())
console.log(xiaoming.eat())
let lisi = new Students3('lisi', 111, 'bbbb')
console.log(lisi.study())
console.log(lisi.speak())
3.3 多态-代码示例
类图关系如图所示:
/**
3. 多态:
*
1. 我们只能通过这种方式去演示, 效果不是特别的好 。
2. 它不像 java 一样, 那样的明显; 很重要的原因就是 JS 是一门弱类型的语言 。
3. 保持子类的灵活性和开放性 。
4. 面向接口编程 。
*/
class Animal1
constructor (name)
this.name = name
saySomething()
console.log('saySomething 多态')
class Dog1 extends Animal1
constructor (name)
super(name)
saySomething()
console.log('____Dog eat DDDD')
class Mouse1 extends Animal1
constructor (name)
super(name)
saySomething()
console.log('____mouse1 eat MMMM')
let d1 = new Dog1('dog')
let m1 = new Mouse1('mouse1')
console.log(d1.saySomething())
console.log(m1.saySomething())
以上代码打印结果:
之前有整理过部分知识点, 现在将整理的相关内容, 验证之后慢慢分享给大家; 这个专题是 “前端设计模式” 的相关专栏; 不积跬步,无以至千里, 戒焦戒躁 。
《前端设计模式》专栏上篇:搭建开发环境
《前端设计模式》专栏下篇: 面向对象-Class类
如果对大家有所帮助,可以点个关注、点个赞; 文章会持续打磨 。
有什么想要了解的前端知识, 可以在评论区留言, 会及时分享所相关内容 。
以上是关于前端 JavaScript 设计模式前奏--面向对象-封装继承多态的主要内容,如果未能解决你的问题,请参考以下文章
前端 JavaScript 设计模式前奏--面向对象-封装继承多态
前端 JavaScript 设计模式前奏--面向对象JQ实例与总结
前端 JavaScript 设计模式前奏--面向对象-Class类
前端 JavaScript 设计模式前奏--面向对象-Class类