JS设计模式之“语言之魂” - 原型模式

news2024/11/28 16:05:46

image.png

前言

当我们学习JavaScript的时候,经常会听到原型(prototype)、原型链(prototype chain)和原型模式(prototype pattern)这些概念,它们之间有什么关联呢?怎么样才能使用好原型模式呢?

一. “语言之魂” - 原型模式

829语言之魂-1.png

概念

原型模式是JavaScript语言的核心机制之一,是JavaScript中最基本的设计模式,因此被称为“语言之魂”,要想在项目开发使用好原型模式,我们必须要了解原型以及原型链的概念,从而才会理解它们和原型模式之间的关系:

  1. 原型(prototype): 原型是JavaScript中对象的一个属性,它允许对象继承属性和方法。每个对象都有一个原型对象,对象可以通过__proto__属性来访问自己的原型对象。原型对象可以包含属性和方法,并且可以通过对象实例访问和共享。

  2. 原型链(prototype chain): 原型链是一通过原型对象之间的链式关系实现对象属性和方法的继承和共享机制。原型链通过对象的__proto__属性指向原型对象,从而形成一个原型的层级链条。访问一个对象的属性或方法时,JavaScript会沿着原型链向上查找,直到找到或者到达原链的末端(Object)。这样,对象可以继承原型对象的属性方法,实现了属性和方法的共享。

  3. 原型模式(prototype pattern): 原型模式是一种设计模式,它利用JavaScript中的原型机制来创建对象。通过定义构造函数,并将构造函数的原对象设置为一个模板,可以基于原型关系来实现对象的创建和属性的继承。通过使用原型模式,可以避免在每个对象实例中重复定义相同的属性和方法,实现了对象的复用和提升性能。

关系

每个对象都有一个原型,并且原型对象之间形成了原型链的关系。原型链通过__proto__属性连接起来,实了属性和方法的继承和共享。原型模式则是利用原型链关系来创建对象,并实现了对象创建和属性继承的设计模式。以可以说,原型是原型链的基础,原型链是实现对象属性和方法继承的机制,而原型模式是基于原型链关系的对象创建属性继承的设计模式。

本文不过多的讲解原型及原型链,感兴趣可以阅读之前文章:深入理解对象的原型和原型链

二. 如何使用原型模式

原型模式.png

当使用原型模式时,我们可以按照以下步骤来创建和使用对象的实例:

  1. 定义原型对象:创建一个包含需要共享的属性和方法的原型对象。

const personPrototype = {
  greet: function() {
    console(`Hello, my name isthis.name}`);
  }
};
  1. 创建新的对象实例并继承型:使用`Object.create方法创建一个新的对象实例,并将其型指向原型对象。

const person1 = Object.create(personPrototype);
person1.name = "Alice";
person1.greet(); // 输出:Hello, my name is Alice
  1. 在新对象实例上定义自己的属性和方法:根据需要,在新的对象实例上定义自己的属性和方法。

const person2 = Object.create(personPrototype);
person2.name = "Bob";
person2.age = 30;
person2.introduce = function() {
  console.log(`I am ${this.name} and I am ${this.age} years old.`);
};
person2.introduce(); // 输出:I am Bob and I am 30 years old.

在上面的例子中,我们首先定义了一个原型对象personPrototype,它包含了一个greet方法。接着,我们使用Object.create()方法创建了两新的对象实例person1person2,并且它们都继承了personPrototype属性和方法。

然后我们为person1实例设置了name属性,并调用了greet方法,输出了相应结果。同样地,我们也为person实例设置了nameage属性,并定义了一个introduce方法,用于打印自我介绍的信息。

通过原型模式,我们根据需要创建多个对象实例,并且这些实例可以共享同一个原型对象的属性和方法。这样既节省了内存空间,也可以实现属性和方法的共享和继承。

注意: 在修改对象实例的属性时,可能会影响原型和其他实例。对于每个对象实例而言,如果希望拥有独立的属性,则需要在对象实例上重新定义这些属性,而不是在原型对象上定义。

三. 深入原型模式的实现原理

JavaScript原型模式的实现原理涉及到原型链的概念。每个对象都有一个隐藏的__proto__属性,指向其原型对象(即它的父对象)。原型链实际上就是通过__proto__属性将对象连接起来形成的一条链。

当我们访问一个对象的属性或方法时,JavaScript引擎首先在当前对象中查找,如果找不到,就会沿着原型链向上查找,直到找到对应的属性或方法,或者到达原型链的顶端(null)为止。

在JavaScript中,当我们创建一个对象时,可以通过Object.create()方法指定一个原型对象,从而创建一个新的对象并继承原型对象的属性和方法。新对象的__proto__指针会指向原型对象。

相应地,我们还可以使用Object.getPrototypeOf()方法获取一个对象的原型对象,或者使用Object.setPrototypeOf()方法设置一个对象的原型对象。

当我们访问对象的属性或方法时,JavaScript引擎会按照以下步骤进行查找:

  1. 首先,它会查找对象本身是否具有相应的属性或方法。如果找到,就直接使用该属性或方法。

  2. 如果对象本身没有相应的属性或方法,它会通过__proto__指针指向的原型对象去查找。如果原型对象具有相应的属性或方法,就直接使用。

  3. 如果原型对象没有相应的属性或方法,引擎会继续沿着原型链向上查找,直到找到对应的属性或方法,或者到达原型链的顶端(null

  4. 如果最终没有找到相应的属性或方法,引擎会返回undefined

这种通过__proto__指针沿着原型链查找的机制,就是JavaScript原型模式的实现原理。通过原型的继承,我们可以实现对象之间的属性和方法的共享。

需要注意的是,当我们修改一个对象的属性时,如果该属性位于原型链上,那么会直接修改原对象的属性值。所以,如果想要每个对象实例都具有独立的属性,应该对象实例上重新定义这些属性,而不是在原型上定义。

总结一下,JavaScript原型模式通过原型链实现对象之间属性和方法继承。通过原型链的机制,我们可以在对象实例之间共享属性和方法,并实现属性和方法的查找。这种机制提高了对象的重用性,并使代码更加简洁和易于维护。

四. 原型模式的应用场景

原型模式在JavaScript中有广泛的应用场景,下面是几种常见的应用场景:

1. 创建对象实例并共享方法

image.png

假设我们需要创建多个具有相同方法的对象实例,比如创建多个 小猫 的对象实例,并且它们都具有相同的方法,比如meow()方法。可以使用原型模式在猫的原型对象上定义meow()方法,并将多个猫的对象实例的原型指向该原型对象,这样它们就共享了相同的方法。

function Cat(name) {
  this.name = name;
}

Cat.prototype.meow = function() {
  console.log(this.name + ' says meow!');
};

var cat1 = new Cat('Tom');
var cat2 = new Cat('Jerry');

cat1.meow(); // 输出:Tom says meow!
cat2.meow(); // 输出:Jerry says meow!

2. 对象的继承

image.png

各种不同形状的图形对象

假设我们有一个基础对象各种个样的图形Shape,它具有getArea()方法。我们想要创建一个继承自ShapeRectangle对象,它具有自己的widthheight属性,并且可以调用父类的getArea()方法。可以使用原型模式,将Shape的实例设置为Rectangle对象的原型,实现继承关系。

function Shape() {}

Shape.prototype.getArea = function() {
  return 0;
};

function Rectangle(width, height) {
  this.width = width;
  this.height = height;
}

Rectangle.prototype = new Shape();
Rectangle.prototype.constructor = Rectangle;

Rectangle.prototype.getArea = function() {
  return this.width * this.height;
};

var rect = new Rectangle(5, 10);
console.log(rect.getArea()); // 输出:50

3. 动态扩展和修改对象

image.png

假设我们有一个基础对象Person,它具有name属性和sayHello()方法。我们想要动态地为对象添加新的方法,例如sayGoodbye()方法。可以直接在Person的原型对象上添加新的方法,而不需要逐个修改现有对象实例。

function Person(name) {
  this.name = name;
}

Person.prototype.sayHello = function() {
  console.log('Hello, my name is ' + this.name);
};

var person = new Person('John');
person.sayHello(); // 输出:Hello, my name is John

Person.prototype.sayGoodbye = function() {
  console.log('Goodbye, ' + this.name);
};

person.sayGoodbye(); // 输出:Goodbye, John

以上是几个典型的JavaScript原型模式的应用场景。通过原型链实现共享属性和方法、继承关系以及动态扩展和修改对象等操作,最终提高代码的重用性和灵活性。

五. 使用原型模式的优缺点

通过以上对原型模式的理解,我们可以总结出使用原型模式有以下的优点:

  1. 重用性和性能优化:原型模式可以将方法和属性共享给所有实例化的对象,避免了在每个对象实例中重复创建相同的方法和属性。这提高了代码的重用性,减少了内存消耗,提升了性能。

  2. 灵活的对象扩展:使用原型模式,可以动态地向原型对象添加、修改和删除属性和方法,这样会自动反映在所有对象实例中。这种灵活性使得可以方便地扩展和修改对象的功能,而无需修改已有的对象实例。

  3. 实现对象的继承:原型模式允许将一个对象设置为另一个对象的原型,从而实现对象之间的继承关系。这样,在子对象上可以访问和重写父对象的属性和方法,实现了类似于面向对象的继承机制。

然而,原型模式也有一些缺点:

  1. 属性共享和修改的潜在问题:当多个对象实例共享一个原型对象时,如果对原型对象的属性进行修改,会影响到所有对象实例。这可能会导致意外的副作用,尤其是在动态修改原型对象时需要谨慎对待。

  2. 难以实现私有属性和方法:在原型模式中,所有的属性和方法都是公开的,无法实现对外隐藏的私有属性和方法。虽然有一些技巧可以模拟私有属性,但并不能真正实现封装性。

  3. 构造函数参数的传递:在原型模式中,构造函数的参数只能通过对象实例的属性赋值来传递。这可能会导致在创建对象实例时的参数传递不够直观和灵活。

综上所述,JavaScript原型模式具有重用性性能优化灵活的对象扩展和对象继承的优点,但也存在属性共享修改问题、难以实现私有和方法,以及构造函数参数传递的局限缺点。在使用原型模式时,需要注意这些特点和限制,确保合理使用以符合需求和预期。

总结

原型模式是JavaScript语言的核心机制之一,同时也是JavaScript中最基本的设计模式,因此被称为“语言之魂”,通过对象的原型实现对象的继承和属性共享,同时基于原型链的机制实现对象的创建和属性的继承,原型模式在JavaScript中具有广泛的应用场景,使用原型模式可以节省内存,增加灵活性和可扩展性等。

但是在使用原型模式时,需要注意以下几点:

  1. 修改对象实例的属性可能会影响原型和其他实例:当我们修改对象实例的属性时,可能会影响到原型和其他实例。如果希望每个对象实例都有独立的属性,需要在对象实例重新定义这些属性,而不是在原型对象上定义。

  2. 原型链的长度和效率:随着原型链的增长,查找属性和方法可能会变慢。因此,需要注意原型链的长度,并合理设计对象的层级关系。

  3. 属性和方法的屏蔽:如果对象实例和原型上都定义了同名的属性或方法对象实例上的属性或方法会屏蔽原型上的对应属性或方法。因此,在设计对象和原型时,需要属性和方法的命名,避免冲突。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2099660.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux 硬件 arm架构

一.ARM: 1.时钟晶振: 在单片机系统里晶振的作用非常大,他结合单片机内部的电路,产生单片机所必须的时钟频率,单片机的一切指令的执行都是建立在这个基础上的,晶振的提供的时钟频率越高,那单片机…

Valley14.2免授权php跨境电商在线商城

6Valley 14.2 Nulled – 多供应商电子商务 – 完整的电子商务移动应用程序、Web、卖家和管理面板 后台可自定义收款,和翻译多国语言,中文需要自己对比翻译!一般用不到中文。毕竟是跨境电商平台 带商家即时通讯,全套带文档和APP双端…

DReg-NeRF: Deep Registration for Neural Radiance Fields论文解读

目录 一、导言 二、NeRF2NeRF 三、相关工作 1、神经辐射场 2、点云配准 3、NeRF配准 四、DReg-NeRF 1、NeRF神经辐射场查询 2、特征提取 3、transformer 4、Decoder 五、损失函数 一、导言 该论文来自于ICCV2023,主要提到一种DReg-NeRF的配准方法&#…

SQL 数据查询

文章目录 3.4.1 单表查询定义特点单表无条件查询单表带条件查询对查询结果进行排序限制查询结果数量 3.4.2 分组查询定义特点:聚集函数GROUP BY短语HAVING子句分组查询小结 3.4.3 连接查询定义特点:等值连接与非等值连接查询自然连接(内连接&…

全局安装react

1、首先安装react脚手架 npm install -g create-react-app2、创建react项目 create-react-app my-app3、 PS D:\桌面\papers\subject> create-react-app my-react-appCreating a new React app in D:\桌面\papers\subject\my-react-app.Installing packages. This might …

基于vue框架的超市订单管理系统16uob(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能:员工,商品分类,商品信息,供货商,入库订单,销售订单,货架信息,盈利信息 开题报告内容 基于Vue框架的超市订单管理系统开题报告 一、研究背景与意义 随着信息技术的飞速发展和电子商务的普及,传统超市管理模式正面临前所未有…

wsl2 安装qt5

sudo apt-get install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools qtcreator -ysudo apt-get install build-essential libfontconfig1 mesa-common-dev -y "qt5-default"从Ubuntu 21.04 存储库中就缺少了该软件包,后续会不会添加暂时未知。 在…

SpringBoot日志使用:Slf4j与Logback

步骤一&#xff1a;引入lombok即可&#xff0c;lombok自带Slf4j注解&#xff08;网上说不用引入api的依赖&#xff0c;若报错可添加&#xff09; <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version&…

数据访问:JPA

文章目录 JPA的由来JPA是什么Spring Data JPA快速上手 JPA的由来 ORM框架能够将Java对象映射到关系型数据库中&#xff0c;能够直接持久化复杂的 Java对象。ORM框架的出现&#xff0c;可以让开发者从数据库编程中解脱出来&#xff0c;把更多的精力放在业务模型与业务逻辑上。目…

Open3D 计算投影的点云的尺寸

目录 一、概述 1.1实现步骤 1.2应用场景 二、代码实现 三、实现效果 3.1原始点云 3.2投影后点云 Open3D点云算法汇总及实战案例汇总的目录地址&#xff1a; Open3D点云算法与点云深度学习案例汇总&#xff08;长期更新&#xff09;-CSDN博客 一、概述 在 Open3D 中&…

electron-vite打包出错

问题&#xff1a;1 electron-vite 安装&#xff0c; 打包下载资源失败&#xff0c;设置国内镜像 由于electron默认打包会从github上下载相关二进制包&#xff0c;众所周知&#xff0c;国内GitHub访问是相当慢的&#xff0c;所以经常会出现下载失败导致打包不成功&#xff0c;…

中兴-ZSRV2路由器-任意文件读取

中兴-ZSRV2路由器-任意文件读取 文章目录 中兴-ZSRV2路由器-任意文件读取免责声明漏洞描述搜索语法漏洞复现修复建议 免责声明 文章中涉及的程序(方法)可能带有攻击性&#xff0c;仅供安全研究与教学之用&#xff0c;读者将其信息做其他用途&#xff0c;由用户承担全部法律及连…

upload-labs闯关攻略

pass-1 提前准备好的一个PHP木马&#xff0c;然后将后缀名改为jpg上传 然后在上传的过程中利用抓包&#xff0c;将抓取到的包里面的后缀jpg改为php如图所示&#xff0c;然后放行 接着我们去访问上传的图片信息&#xff0c;如下图所示就为成功 pass-2 提前准备好的一个PHP木马…

线程池相关知识点

线程池是什么相信大家都是知道的&#xff0c;所以这里就不做解释了&#xff0c;直接看相关知识点吧。 初始化线程池方法 继承ThreadPool 实现Runnable 实现Callable 接口 FutureTask &#xff08;可以拿到返回结果&#xff0c;可以处理异常&#xff09; 核心参数 corePoo…

[米联客-XILINX-H3_CZ08_7100] FPGA程序设计基础实验连载-23 VTC视频时序控制器设计

软件版本&#xff1a;VIVADO2021.1 操作系统&#xff1a;WIN10 64bit 硬件平台&#xff1a;适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA 实验平台&#xff1a;米联客-MLK-H3-CZ08-7100开发板 板卡获取平台&#xff1a;https://milianke.tmall.com/ 登录“米联客”FPGA社区 http…

Javascript LeeCode选题(汉诺塔求解)

LeeCode选题 汉诺塔递归求解move移动函数hanoi函数main方法测试代码&#xff1a;代码实现 汉诺塔递归求解 汉诺塔介绍&#xff1a; 汉诺塔的的图形&#xff08;从上到下1&#xff0c;2&#xff0c;3个&#xff09;实现&#xff1a; 这里我们可以看到因为必须要将第n个移动到…

数据结构与算法 第7天(树和森林)

树 树的存储结构 双亲表示法 找双亲容易&#xff0c;找孩子不容易 孩子链表法 把每个结点的孩子节点排列起来&#xff0c;看成一个线性表&#xff0c;用单链表存 找孩子容易&#xff0c;找双亲难 带双亲孩子链表法 给孩子链表加一个参数&#xff0c;存双亲的下标 孩子兄…

系统思考—盲点

突‮盲破‬点&#xff0c;解‮合锁‬作潜能——JSTO 的‮能高‬碰撞&#xff01; 在今天的JSTO会议中&#xff0c;我们在Check In环‮分节‬享了近1-2周‮实的‬践和反思。这‮环个‬节不仅‮助帮‬大家‮享共‬了更多的‮息信‬和资源&#xff0c;还‮效有‬促进了彼‮间此‬…

jenv 一款macos下的开源JAVA多版本环境安装管理切换工具

一个用于macos/linux下的多版本JAVA环境管理工具 -- jenv, 这款工具和 pyenv 类似,都是基于shell脚本开发的. 可以方便的管理 多个java环境版本. jenv安装 git clone https://gitee.com/tekintian/jenv.git ~/.jenv jenv环境配置 将下面的代码加入都你的 ~/.bash_profil…

堆垛机知识介绍:附图

导语 大家好&#xff0c;我是社长&#xff0c;老K。专注分享智能制造和智能仓储物流等内容。 完整版文件和更多学习资料&#xff0c;请球友到知识星球【智能仓储物流技术研习社】自行下载。 这份文件是关于堆垛机的介绍&#xff0c;内容包括堆垛机的概念、分类、工作原理、结构…