JavaScript 的面向对象基础,设计模式中的原型模式(设计模式与开发实践 P2)

2023-09-14 16:18:42

在学习 JS 设计模式之前需要了解一些设计模式基础,如果不是 JavaScript 用户可以直接跳到设计模式篇的讲解~

1.1 动态类型语言和鸭子类型

编程语言按照数据类型分为:

  • 动态类型语言
  • 静态类型语言

动态类型更专注于业务逻辑,缺点是无法保证变量的类型

由于无法保证类型,所以我们可以尝试调用任何对象的任何方法,不用去考虑他是否原本被设计为拥有该方法。这一切都建立在 鸭子类型 的概念上,通俗说法是:“如果他走起来是鸭子,叫起来也是鸭子,那他就是鸭子”

例如:在这种情况下,我们不需要检测实体的类型,如果这个实体具有 singing 这个功能,他就可以被当作是 duck 来使用

var duck = {
	function singing(){ ... }
}

var dog = {
	function singing(){ ... }
}

1.2 多态

多态的含义是:同一个操作在不同的类型上面,可以产生不同的解释,不同的执行效果。

例如这段代码,虽然产生了多态性,但这样的多态性是不让人满意的,如果要加入一个新的动物,你又要修改 makeSound 这个函数,到最后很可能变的巨大!

多态的思想是,将 做什么、谁去做、怎样做 分离开,也就是把 不变的事情和可变的事情 分开,使得程序是可生长的,又符合 开放-封闭 原则

var makeSound = function(animal) {
	if (animal instanceof Duck) {
		console.log("噶");
	} else if (animal instanceof Dog) {
		console.log("汪");
	}
}

var Duck = function() {}
var Dog = function() {}

makeSound(new Duck())
makeSound(new Dog())

让我们试试改写代码,这是封装的不变的部分:

var makeSound = function(animal) {
	animal.sound();
}

接下来完成可变的部分,如果后续要添加其他动物,只需要追加代码,而不用改变 makeSound 了:

var Duck = function() {}
Duck.prototype.sound = function() {
	console.log("噶");
}

makeSound(new Duck())

如果你使用 Java,按照上面的写法你会发现一个问题:

public class Duck{
	public void makeSound(){
		System.out.println("噶");	
	}
}

public class AnimalSound{
	public void makeSound(Duck duck) {
		duck.makeSound();
	}
}

public class Main{
	public static void main(){
		AnimalSound sound = new AnimalSound();
		Chicken chicken = new Chicken();
		sound.makeSound(chicken);
	}
}

sound.makeSound 固定接收了 Duck 类型参数,没有办法让他接受动态类型,这就需要继承来解决问题了,通过 abstract 抽象类,让 Duck 和 Dog 都继承 Animal 类型~

public abstract class Animal{
	abstract void makeSound();
}

public class Chicken extends Animal...

让我们再来一段代码理解 js 和 java 中的区别:

var googleMap = {
	show: function() { ... }
}
var baiduMap = {
	show: function() { ... }
}

var renderMap = function (type) {
	if (type instanceof baiduMap) { ... }
	else if (type instanceof googleMap) { ... }
}

如果要加一个 bing 地图,此时又要修改 renderMap,在 JS 中则这样改写即可,可以看到 JS 自带了这种动态类型转换的属性,因为 JS 只关注实现,不再关注类型细节了:

var renderMap = function(map) {
	if (map.show instanceof Function) map.show();
}

renderMap(googleMap);
renderMap(baiduMap);

后续的许多设计模式都离不开多态,而 JS 有时自带这种属性可以利用高阶函数很快实现~

1.3 封装

封装一般是指封装数据和封装实现,还有封装类型和封装变化。

封装数据

许多语言通过 private、protected 等关键字来提供访问权限,但 JS 没有这样的系统,我们只能通过变量的作用域来实现封装特性

var obj = (function(){
	var _name = "sven";
	return {
		getName: function(){
			return _name;
		}
	}
})

封装实现

上面讲的封装指的是数据层面的封装,这是一种比较狭义的定义

封装的目的是将信息隐藏,封装应该被视为“任何形式的封装“,也就是说,封装还应该隐藏实现细节、设计细节以及隐藏对象的类型

封装使得对象内部的变化对其他对象而言不可见,对象对他自己的行为负责,其他对象都不关心它的内部实现,这使得对象之间耦合变得松散

封装类型

封装类是静态语言中重要的一种封装方式。通过抽象类和接口进行,把对象的真实数据隐藏在抽象类或者接口之后。相比对象的类型、客户只需要关注对象的行为

1.4 原型模式和基于原型继承的 JavaScript 对象系统

一般的面向对象语言中,类和对象是关键。而在 javascript 这样的原型编程的思想中,类不是必需的,对象是通过克隆另一个对象得来的。

原型模式不单是一种设计模式,还是一种编程泛型。原型模式实现的关键是语言是否拥有 clone 方法,他的优点就是可以减少类的初始化、删除、复刻时产生的大量运算

  • 所有数据都是对象
  • 对象不是通过实例化类,而是通过找到对象作为原型并克隆他

C# 原型模式

这里先使用 C# 举例,下面是类的实现:

using System;

// 基类
public abstract class Shape
{
    public int X { get; set; }
    public int Y { get; set; }

    public abstract Shape Clone();
}

// Circle 派生类
public class Circle : Shape
{
    public int Radius { get; set; }

    public override Shape Clone()
    {
        return (Shape)MemberwiseClone();
    }
}

下面是原型模式的应用,可以看到通过新的数据 clonedCircle 通过克隆得来:

class Program
{
    static void Main(string[] args)
    {
        Circle circle = new Circle();
        circle.X = 10;
        circle.Y = 20;
        circle.Radius = 30;

        Circle clonedCircle = (Circle)circle.Clone();
        Console.WriteLine($"Circle: X={clonedCircle.X}, Y={clonedCircle.Y}, Radius={clonedCircle.Radius}");

        Console.ReadLine();
    }
}

JS 原型模式

在 JS 中原型模式这样构建,而且拥有了2个新的特性:

  1. 对象会记住他的原型
  2. 如果对象无法响应某个请求,对象会尝试委托原型进行响应
// 原型对象
const shapePrototype = {
  x: 0,
  y: 0,
  clone() {
    // 创建一个新对象,并使用原型对象的属性进行初始化
    const clone = Object.create(this);
    return clone;
  }
};

// Circle 构造函数
function Circle(radius) {
  this.radius = radius;
}

// 将 shapePrototype 设置为 Circle 构造函数的原型
Circle.prototype = shapePrototype;

// 创建 Circle 对象的实例
const circle = new Circle(10);
circle.x = 20;
circle.y = 30;

// 克隆 Circle 对象
const clonedCircle = circle.clone();

console.log(clonedCircle.x);  // 输出: 20
console.log(clonedCircle.y);  // 输出: 30
console.log(clonedCircle.radius);  // 输出: 10
更多推荐

虹科案例 | LIN/CAN总线汽车零部件测试方案

文章来源:虹科汽车电子点此阅读原文虹科的LIN/CAN总线汽车零部件测试方案是一款优秀的集成套装,基于Baby-LIN系列产品,帮助客户高效完成在测试、生产阶段车辆零部件质量、功能、控制等方面的检测工作。1、汽车零部件测试的重要性?汽车零部件的测试对于确保汽车的安全性、功能性和可靠性起着至关重要的作用。LIN/CAN通

驱动开发DAY4

驱动代码#include<linux/init.h>#include<linux/module.h>#include<linux/cdev.h>#include<linux/fs.h>#include<linux/device.h>#include<linux/uaccess.h>#include<linux/slab

【Pytest实战】Pytest+Allure+Jenkins自动化测试框架搭建

😄作者简介:小曾同学.com,一个致力于测试开发的博主⛽️,主要职责:测试开发、CI/CD如果文章知识点有错误的地方,还请大家指正,让我们一起学习,一起进步。😊座右铭:不想当开发的测试,不是一个好测试✌️。如果感觉博主的文章还不错的话,还请点赞、收藏哦!👍之前分享过Pytest基础知识,可参考Pytest实战专栏

创建Scrapy项目

创建Scrapy项目的步骤如下:安装Scrapy:在终端或命令提示符中运行以下命令来安装Scrapy:pipinstallscrapy创建Scrapy项目:在终端或命令提示符中,使用以下命令创建一个新的Scrapy项目:scrapystartproject<project_name>其中,<project_name>是

【详细教程hexo博客搭建】1、从零开始搭建一个能用的博客

1、开始2.环境与工具准备本教程主要面对的是Windows用户操作系统:Windows10NodeGitHexo文本编辑器(强烈推荐VSCODE)GitHub帐号一个域名(强烈推荐买个域名)云服务器(可选)3.Node的安装打开Node官网,下载和自己系统相配的Node的安装程序,否则会出现安装问题。下载地址:Down

Vue-devTools安装—创建项目方法2 ui创建——Vue指令综合案例——汽车品牌管理

目录项目源代码:一、vue-devTools安装二、案例功能实现1、新建项目(ui创建)2、cnpm导入项目依赖库3、删除不需要的代码结构:4、修改代码结构5、添加汽车品牌插件安装bootstrap的提示功能添加bootstrap样式6、删除汽车品牌7、查询汽车品牌(过滤)项目源代码:Vue指令综合案例——汽车品牌管理

(二十九)大数据实战——kafka集群节点服役与退役案例实战

前言本节内容是关于kafka集群节点的服役与退役,从而实现kafka集群的缩容与扩容。在开始本节内容之前,我们要预先安装好kafka集群,并准备一台空余的服务器用来完成我们扩容与缩容的案例。关于kafka集群的安装内容这里不在赘述,相关内容请查看作者往期博客内容。正文从hadoop103克隆一台空闲服务器hadoop1

QT信号槽实现原理

定义Q_OBJECT宏在宏中声明了几个重要的成员变量及成员函数,包括声明了一个只读的静态成员变量staticMetaObject,以及3个public的成员函数staticconstQMetaObjectstaticMetaObject;virtualconstQMetaObject*metaObject()const

低代码之IVX

低代码之IVX一、如何去分析不同的低代码平台🧝‍♂️1.看自己的网站和平台是不是通过自己的低代码/无代码平台生成的2.生成源代码的能力3.可视化的逻辑编排二、低代码平台未来的发展方向👨‍🌾三、优质低代码平台介绍👨‍💼1.什么是iVX:2.iVX和其它低代码平台的区别上手步骤1.1iVX线上集成环境进入1.2创

python爬虫——爬取豆瓣top250电影数据(适合初学者)

前言:爬取豆瓣top250其实是初学者用于练习和熟悉爬虫技能知识的简单实战项目,通过这个项目,可以让小白对爬虫有一个初步认识,因此,如果你已经接触过爬虫有些时间了,可以跳过该项目,选择更有挑战性的实战项目来提升技能。当然,如果你是小白,这个项目就再适合不过了。那么就让我们开始吧!目录一、实战1.对豆瓣网网站进行Ajax

数据库设计与建模

数据库设计与建模1数据库设计的三范式2数据库建模2.1建模工具2.2使用pd建模1数据库设计的三范式三范式:1.第一范式(1NF):确保每一列的原子性(做到每列不可拆分)2.第二范式(2NF):在第一范式的基础上,非主字段必须依赖于主字段(一个表只做一件事)3.第三范式(3NF):在第二范式的基础上,消除传递依赖反三范

热文推荐