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

2023-10-26

在学习 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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐

  • 更多的服务商从业者都开始关注刷脸支付

    刷脸支付就是通过人脸进行支付 简单来说消费者在接入蜻蜓的商家结账付款时 只需将脸面对蜻蜓上的摄像头即可完成支付 在这一过程中用户甚至不需要用手机 支付效率相比扫码支付更加的高效 此外由于支付宝刷脸支付是基于3D人脸识别技术 在安全也要比扫码
  • 【机器学习】编码、创造和筛选特征

    在机器学习和数据科学领域中 特征工程是提取 转换和选择原始数据以创建更具信息价值的特征的过程 假设拿到一份数据集之后 如何逐步完成特征工程呢 文章目录 一 特性类型分析 1 1 数值型特征 1 2 类别型特征 1 3 时间型特征 1 4 文
  • SpringMVC的全注解开发

    文章目录 一 spring mvc xml 中组件转化为注解形式 二 DispatcherServlet加载核心配置类 三 消除web xml 一 spring mvc xml 中组件转化为注解形式 跟之前全注解开发思路一致 xml配置文件
  • 2.c语言中将两个整数相加

    上一个文章 我讲述了如何输出文字 接下来我就来讲讲有一丢丢难度的算法了 这个算法就是学习如何将两个整数相加 这是学习如何编写一个计算机软件的第一步 现在我就为你们来一一讲解 代码如下 include
  • StringBuffer integer

    StringBuffer 一 1 StringBuffer是一个容器 而容器的特点是可以修改 基本操作是增添删改 这也是它与StringBuffer的主要区别 2 线程安全 可变的字符序列 是一个字符缓冲区 也是final型 不能被继承 3
  • java基础-垃圾收集器及其回收算法的介绍

    文章目录 前言 一 垃圾收集器管理的区域 二 垃圾收集器的回收步骤 1 阶段一 判断对象是否存活 2 阶段二 筛选 三 垃圾收集算法 1 分代收集理论 理论基础 2 标记 清除算法 3 标记 复制算法 4 标记 整理算法 四 补充 前言 最
  • v8所有例子在里面

    https github com nodejs nan blob master doc methods md
  • 线程间怎么交换数据_在LLVM后端实现跨通道数据搬移

    作者 汪岩 AMD GPU的每个CU有一个64kB的存储空间 称为本地数据共享 Local Data Share LDS 用于同一计算单元中的work group内各个work item之间的低延迟通信和数据共享 LDS配置为32个bank
  • 为了通过面试,要刷多少道leetcode题?一文解答你的疑惑。

    想要学习算法 应付笔试或者应付面试手撕算法题 相信大部分人都会去刷 Leetcode 有读者问 如果我在 leetcode 坚持刷它个 500 道题 以后笔试 面试稳吗 这里我说下我的个人看法 我认为不稳 下面说说为啥不稳以及算法题应该如何
  • Acwing 908. 最大不相交区间数量

    include
  • VTM中YUV-PSNR的计算

    名词解释 参见文章cnblogs 什么是SAD SAE SATD SSD SSE MAD MAE MSD MSE PSNR的计算 较为标准的MSE计算公式和PSNR计算公式如下 引用链接 CSDN 图像的峰值信噪比 PSNR 的计算方法 这
  • linux用户态使用gpio中断方法

    一 用户空间gpio的调用文件 用户空间访问gpio 即通过sysfs接口访问gpio 下面是 sys class gpio目录下的三种文件 export unexport文件 gpioN指代具体的gpio引脚 gpio chipN指代gp
  • 数字化转型的成熟度模型

    来啦 坐 我是冠军 数据赋能 IT团队技术管理实战 作者 四季逗文风创始人 这是 数字化转型系列 第五篇 数字化转型的成熟度模型 一句话解释下 要想搞清楚企业数字化转型的目标 就需要确定企业现在处于什么数字化阶段 只有如此 才可以根据现状目
  • Golang

    欢迎关注 全栈工程师修炼指南 公众号 点击 下方卡片 即可关注我哟 设为 星标 每天带你 基础入门 到 进阶实践 再到 放弃学习 专注 企业运维实践 网络安全 系统运维 应用开发 物联网实战 全栈文章 等知识分享 花开堪折直须折 莫待无花空
  • 2023校招联想汽车C++开发一面

    飞书面试 C 开发 全程无手撕代码 开放性面试 1 翻译英文 给几段英文句子 是和专业相关的 要求英译汉 2 给好几组计算机专业名词 从中选择最熟悉的 进行解释 比如https dns smtp等等 3 一个电路 求其中一个电阻电压 4 代
  • NB!更方便Xshell本地密码破解工具

    工具介绍 XshellCrack是基于SharpXDecrypt的二次开发 用go语言重写 增加了注册表查询设置 更方便xshell本地密码破解 关注 Hack分享吧 公众号 回复关键字 230717 获取下载链接 工具使用 Usage r
  • JavaScript--修改 HTML 元素

    这些是一些用于修改 HTML 元素的常见方法 1 document createElement element 创建 HTML 元素节点 可以使用这个方法创建一个新的 HTML 元素 例如 document createElement di
  • Docker进阶学习:Compose配置编写规则

    docker compose yaml 是Compose的核心 以下是compose的官方文档地址 https docs docker com compose compose file compose file structure and
  • rabbitmq集群故障处理

    rabbitmq集群故障处理 故障现象 rabbitmq启动失败 手动kill掉rabbit的后台进程 杀完后进程又会自动起来 故障处理 1 手动更改rabbitmq故障节点的erl crash dump为erl crash dump ba
  • JavaScript 的面向对象基础,设计模式中的原型模式(设计模式与开发实践 P2)

    文章目录 1 1 动态类型语言和鸭子类型 1 2 多态 1 3 封装 封装数据 封装实现 封装类型 1 4 原型模式和基于原型继承的 JavaScript 对象系统 C 原型模式 JS 原型模式 在学习 JS 设计模式之前需要了解一些设计模