JavaScript原型继承与面向对象编程思想

news2024/12/25 12:49:08

原型继承与面向对象编程思想

        在JavaScript中,原型(prototype)、构造函数(constructor)和实例对象(instance)是面向对象编程中的重要概念,并且它们之间存在着紧密的关系。

  • 原型(prototype):原型是JavaScript中对象之间关联的一种机制。每个JavaScript对象(除了null和undefined)都有一个原型对象,它包含了对象的属性和方法。当访问一个对象的属性或方法时,如果对象本身没有定义该属性或方法,JavaScript引擎会通过原型链向上查找,直到找到对应的属性或方法为止。同理,原型链是由对象的原型对象构成的链式结构,通过这种机制,对象可以继承原型对象的属性和方法。
  • 构造函数(constructor):构造函数是用于创建对象的函数。JavaScript中,可以通过定义一个函数并使用new关键字来创建对象的实例。构造函数定义了对象的初始状态和行为,并且可以在创建实例时对其进行初始化。构造函数可以包含属性和方法,并且可以使用this关键字引用要创建的实例对象。
  • 实例对象(instance):实例对象是通过构造函数创建的对象,它具有构造函数定义的属性和方法。每个实例对象都是独立的,它们可以根据需要修改自己的属性值,并且可以调用构造函数中定义的方法。实例对象通过原型链与构造函数的原型对象关联在一起,从而实现属性和方法的继承。

JavaScript 原型与原型链

prototype: 每一个 函数都有一个特殊的属性叫做原型,指向 该函数的原型对象,原型对象上定义的属性和方法,可以被该函数的实例所共享。
constructor: 相比于普通对象的属性,原型对象prototype属性本身会有一个指向构造函数的指针,即constructor属性, 指向创建该原型对象的构造函数。prototype包含constructor。
__proto__: 每一个对象都有一个__proto__属性, 指向它的构造函数的prototype属性所指向的对象,也就是该对象的原型。
function Car(make, model, year) {//Car是一个构造函数  
  this.make = make;  
  this.model = model;  
  this.year = year;  
}  
var myCar = new Car('Toyota', 'Corolla', 1995);//myCar是Car的一个实例  
// 原型链结构如下:  
// myCar -> Car.prototype -> Object.prototype  
// 查看各个属性的指向  
console.log(myCar.__proto__ === Car.prototype); //true 因为myCar是由Car构造函数创建的
console.log(Car.prototype.constructor === Car); //true 因为Car.prototype是Car构造函数的原型对象 
console.log(myCar.constructor === Car); //true 因为myCar继承自Car的原型  

//**指向Car构造函数对象本身的原型,Car是一个函数,它的原型是Function.prototype(Function.prototype是所有函数对象的默认原型)
console.log(Object.getPrototypeOf(Car) === Function.prototype); //true 
console.log(Car.__proto__===Function.prototype) //true 
//**指向通过Car构造函数创建的实例的原型,是一个包含共享属性和方法的对象(是一个独立的对象)
console.log(Object.getPrototypeOf(Car.prototype) === Object.prototype); 
console.log(Car.prototype.__proto__===Object.prototype) //true 

// 沿着原型链查找属性  
console.log(myCar.toString); //函数,来自Object.prototype
Car.prototype.say=function(){console.log("hi")};//给Car的原型对象添加say方法
myCar.say()//Hi

原型

        在JavaScript中,每个函数都有一个prototype属性,这个属性指向函数的原型对象,这个原型对象包含了可以从该对象实例上访问的属性和方法。此外,每个实例对象都有一个__proto__属性它指向这个对象的原型对象(构造该实例的函数的原型对象)

        当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

        当给一个对象添加一个属性或方法时,它会先查找这个对象本身是否有这个属性或方法。如果有,它就会直接覆盖;如果没有,它会把这个属性或方法添加到这个对象本身;这个过程叫做属性或方法的赋值;赋值后,这个对象就拥有了这个属性或方法。

在JavaScript中,每个对象都有一个关联的原型(prototype),它是一个对象或 null。原型对象包含共享的属性和方法,可以被其他对象继承,而对象则可以访问这些属性和方法。

原型链

原型链的工作原理可以概括为

当访问一个对象的某个属性时,会先在这个对象本身属性上查找,

如果没有找到,则会到自身的__proto__上查找,而实例的__proto__指向其所属类的prototype,即会在类的prototype上进行查找,

如果还没有找到,继续到类的prototype的__proto__中查找,即Object.prototype,

如果在Object.prototype中依旧没有找到,那么返回null。

        原型链允许对象继承其他对象的属性和方法,而不需要在每个对象中重复定义这些属性和方法,从而提高了代码的复用性和效率。

  • 一切对象都是继承自Object对象,Object对象直接继承根源对象null
  • 一切的函数对象(包括Object对象),都是继承自Function对象
  • Function对象的__proto__会指向自己的原型对象,最终还是继承自Object对象
function exampleFunction() {} // 一个函数对象
// exampleFunction是一个函数对象,它的__proto__属性指向Function.prototype
console.log(exampleFunction.__proto__ === Function.prototype); // true  
// Function.prototype本身也是一个对象,它的__proto__属性指向Object.prototype 
console.log(Function.prototype.__proto__ === Object.prototype); // true  
// 原型链:exampleFunction -> Function.prototype -> Object.prototype  

        在JavaScript中,每个对象都有一个指向它的构造函数的指针,而每个构造函数都有一个指向它的原型对象的指针。当创建一个新的对象实例时,这个实例的__proto__指针会指向构造函数的原型对象,同时,构造函数的prototype属性也会指向这个原型对象。这样,就形成了一个构造函数、原型和实例之间的三角关系,这种三角关系的工作方式如下:

  • 当创建一个新实例时,它的__proto__指针被设置为构造函数的prototype对象(即原型对象)
  • 构造函数的prototype属性指向原型对象,这个原型对象包含了所有实例共享的属性和方法。
  • 原型对象的constructor属性指向构造函数本身,形成了一个闭环。

原型链是由对象的原型构成的链状结构。当试图访问对象的属性或方法时,如果对象本身没有定义,JavaScript引擎就会沿着原型链向上查找,直到找到相应的属性或方法,或者链结束(即原型为null)。

JavaScript 原型实现继承(原型继承、构造函数继承、组合继承)

        JavaScript的原型继承与其他一些面向对象语言的类继承有所不同。在JavaScript中,没有显式的类声明和继承关键字,而是通过原型链和构造函数来实现继承。

下面通过一个父类Animal来举例子:

function Animal(name) {
  this.name = name || 'Animal';
}
Animal.prototype.sayHello = function() {
  console.log( "'Hello, I'm'" + this.name);
};

原型继承

        原型链继承通过将父类的实例作为子类的原型,从而实现继承。(通过将一个构造函数的实例赋值给另一个构造函数的原型来实现继承关系)

//通过这种方式,Cat 继承了 Animal 的属性和方法。
function Cat(color) {
  this.color = color
}
// 将Animal的实例赋值给Cat的原型
Cat.prototype = new Animal();
Cat.prototype.name = 'Whiskers';
let myCat = new Cat();
myCat.sayHello();  // 输出:Hello, I'm Whiskers
优点:
①实例既是子类的实例,也是父类的实例,继承关系非常纯粹。
②子类可以访问父类新增的原型方法和属性。
③实现简单,易于理解和实现。
缺点:
①创建子类实例的时候,不能传参数。无法向超类传递参数。
②子类无法在构造器中新增属性和方法,只能在实例化后添加。
③无法实现多继承。
④所有实例共享来自原型对象的属性,包括引用属性。
原型链继承适用于简单的继承关系和单一继承需求的场景。

构造函数继承

        因为JS中没有类这个概念,所以JS的设计者使用了构造函数来实现继承机制。JS通过构造函数来生成实例。但在构造函数中通过this赋值的属性或者方法,是每个实例的实例属性以及实例方法,无法共享公共属性。所以又设计出了一个原型对象,用来存储构造函数的公共属性以及方法

        构造函数继承通过在子类构造函数中调用父类构造函数,复制父类的实例属性给子类,实现对父类属性的继承。

function Dog(name, color) {
  //使用apply()或call()方法以新创建的对象(即new操作符调用子构造函数创建的那个对象)为上下文执行父类构造函数(以普通函数的形式)
  Animal.call(this, name);
  this.color = color;
}
let myDog = new Dog("Buddy", "brown");
myDog.sayHello();  // 输出:Uncaught TypeError: myDog.sayHello is not a function
优点:
①解决了原型链继承中子类实例共享父类引用属性的问题。
②可以在创建子类实例时向父类传递参数。
③支持多继承,可以调用多个父类构造函数。
缺点:
①没有把子类和父类的原型关联起来,子类实例并不是父类的实例,只是子类的实例。
无法继承父类的原型属性和方法,子类没法使用父类的原型方法。
③无法实现函数复用,每个子类都有父类实例函数的副本,影响性能。
构造继承适用于需要继承实例属性、避免引用属性共享以及多继承的场景。

组合继承(原型继承+构造继承)

        组合继承结合了原型继承和构造继承的优点,通过调用父类构造函数来继承父类的属性,并将父类实例作为子类原型,实现函数复用。

function Bird(name, wingspan) {
  Animal.call(this, name);
  this.wingspan = wingspan;
}
// 使用Object.create创建新对象,避免引用类型属性共享
Bird.prototype = Object.create(Animal.prototype);
Bird.prototype.constructor = Bird;  // 修复构造函数指向
let myBird = new Bird("Feathers", 50);
myBird.sayHello();  // 输出:Hello, I'm Feathers
特点:
①继承父类实例属性和方法。
②继承父类原型属性和方法。
③既是父类的实例,也是子类的实例。
缺点:
①调用了两次父类构造函数,影响性能。
组合继承适用于大多数场景,是一种常用的继承方式。

ES6中的类和继承

        ES6引入了class 关键字,使得面向对象编程更加直观。但本质上仍然使用原型链实现继承。

class Fish extends Animal {
  constructor(name, type) {
    super(name);
    this.type = type;
  }
  swim() {
    console.log(this.name + " is swimming.");
  }
}
let myFish = new Fish("Goldie", "Goldfish");
myFish.sayHello();  // 输出:Hello, I'm Goldie
myFish.swim();  // 输出:Goldie is swimming.

JavaScript new创建对象原理详解

        任何函数只要是使用new操作符调用的就是构造函数,而不使用new操作符调用的函数就是普通函数。构造函数是用于创建对象的函数,通过构造函数可以定义对象的属性和方法,原型是一个对象,构造函数通过prototype属性与原型关联。

构造函数的两种属性类型:

  • 实例属性在函数内部通过this设置的都是实例属性,每个实例对象都有自己的一份实例数据,不会相互影响
  • 原型属性在函数外部通过.prototype设置的都是原型属性,是所有实例对象共享的,如果是引用值,那么一个实例修改会导致所有实例都受到影响

        使用new执行函数的时候,new会帮我们在函数内部加工this,最终将this作为实例返回给我们,可以方便我们调用其中的属性和方法。

  • ①在内存中创建一个新对象
  • ②将该新对象内部的[[Prototype]]特性__proto__连接到(赋值为)该构造函数的prototype属性(将构造函数的原型对象赋值给新对象的原型对象,对象与构造函数之间并没有直接的关联)
  • ③将函数内部的this指向这个新创建的对象(将构造函数的作用域赋值给新对象)
  • 执行构造函数内部的代码(为新对象添加实例属性和实例方法)
  • ⑤如果构造函数返回一个非原始类型(即对象或函数)的值,则返回该对象;否则,将this作为返回值,返回刚创建的新对象
const plainObject = {};//1.创建空的简单js对象
plainObject.__proto__ = function.prototype;//2.将空对象的__proto__连接到该函数的prototype
this = plainObject;//3.将函数的this指向新创建的对象
return this//4.返回this

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

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

相关文章

ElasticSearch之bool多条件查询

写在前面 在实际的业务场景中,不可能只是简单的单值查询 ,更多的是n个条件的综合查询,就像下面的搜索: 针对这种场景我们就需要依赖于bool查询了,本文就一起来看下这部分的内容。 1:bool查询介绍 bool查…

第十一天-Excel的操作

目录 1.xlrd-Excel的读模块 安装 使用 获取工作簿 读取工作簿的内容 xlsxwriter-Excel的写模块 安装 使用 生成图表 add_series参数 图表的样式 demo:生成图表 Excel的操作在python中有多个模块,为了能够快速使用,选择了相对简单…

CloudFlare免费内网穿透

介绍 Cloudflare Tunnel是Cloudflare零信任网络的一个产品,用于打通企业、员工、设备之间的边界,从而摒弃掉VPN之类的过时技术(其实也不是过时,只不过是相对来说安全性、可控性较差) 通过Cloudflare Tunnel&#xff0c…

国家建筑装配式内装产业基地在沪成立,副主任单位优积科技协同助推绿色低碳循环发展

上海市室内装饰行业协会装配式内装产业专业委员会成立大会暨“国家建筑装配式内装产业基地”项目启动会于3月21日下午1点在上海光大酒店隆重举行。出席此次活动的包括市装协会长徐国俭,市装协党支部书记兼秘书长丛国梁,市装协装配式内装委主任顾泰昌&…

java面试设计模式篇

面试专题-设计模式 前言 在平时的开发中,涉及到设计模式的有两块内容,第一个是我们平时使用的框架(比如spring、mybatis等),第二个是我们自己开发业务使用的设计模式。 面试官一般比较关心的是你在开发过程中&#…

TiDB离线部署、Tiup部署TiDB

先做tidb准备工作: 部署 TiDB 前的环境检查操作:TiDB 环境与系统配置检查 | PingCAP 文档中心 1.查看数据盘 fdisk -l (2,3)本人的分区已经是 ext4 文件系统不用分区,具体官方文档的分区: 4.查看数据盘…

2024生物发酵产品与技术装备展的创新魅力-东特科技

参展企业介绍 温州东特科技有限公司是一家集设计、生产、销售及服务为一体的卫生级流体设备企业。专业从事各种乳食品、制药、化工、啤酒设备、不锈钢卫生级阀门,管件,视镜,及非标配件定制等产品的销售与服务。先进的设计理念专业的技术优势…

SpringBoot+PDF.js实现按需分片加载预览(包含可运行示例源码)

SpringBootPDF.js实现按需分片加载预览 前言分片加载的效果前端项目前端项目结构前端核心代码前端项目运行 后端项目后端项目结构后端核心代码后端项目运行 项目运行效果首次访问分片加载 项目源码 前言 本文的解决方案旨在解决大体积PDF在线浏览加载缓慢、影响用户体验的难题…

路飞项目--06

redis介绍和安装 # 数据库: 关系型数据库:mysql、oracle、postgrasql、sqlserver、sqlite IBM:服务器 Oracle:数据库 达梦 EMC:存储 非关系型数据库: redis、mongodb、es…

一文读懂:AWS 网络对等互连(VPC peering)实用操作指南

VPC peering connection-网络对等互连在您的 Atlas VPC 和云提供商的 VPC 之间建立私有连接。该连接将流量与公共网络隔离以提高安全性。本篇文章有VPC peering的操作指南以及价格等信息。如还有疑问请联系我们MongoDB的销售,客户成功经理或解决方案架构师。 1 使用…

CPU漏洞之Meltdown

1.前言 计算机系统的安全性从根本上依赖于内存隔离,例如内核(Kernel)地址范围被标记为不可访问,并对用户访问加以限制和保护,因此操作系统确保了用户程序不能访问彼此的内存或内核内存。这种内存隔离是我们计算机环境的基石,它允…

【mysql】1000w数据量的分页查询SQL,如何优化提升性能?

文章目录 优化场景特别注意!!!有前提,谨慎使用 优化场景 当表数据量非常大时,需要进行分页查询如果慢的时候,可以考虑优化下。 假设一页展示10条,查询第10w条后面的数据时候变慢了… 优化思路&…

抖音短视频:表情包账号的魅力与运营之道以及制作与工具

在短视频的浪潮中,抖音以其独特的创意和趣味性成为了年轻人的最爱。其中,表情包账号更是凭借其生动、形象的表现方式,赢得了众多用户的青睐。本文将深入探讨抖音短视频表情包账号的魅力所在以及如何有效运营。 一、表情包账号的独特魅力 情…

C# 1.消息队列MQ使用场景--图文解析

为什么使用消息队列MQ(Message Queue)? 消息队列有什么优点和缺点? Kafka(大数据日志采集)、ActiveMQ(最早的MQ--目前使用较少)、RabbitMQ(开源,中小型企业使用足够)、RocketMQ(阿里开发,大型企业适用) 都…

windows Server下Let‘s Encrypt的SSL证书续期

一、手动续期方法: 暂停IIS服务器 --> 暂时关闭防火墙 --> 执行certbot renew --> 打开防火墙 --> 用OpenSSL将证书转换为PFX格式-->pfx文件导入到IIS --> IIS对应网站中绑定新证书 --> 重新启动IIS -->完成 1、暂停IIS服务器 2、暂时关闭…

冲击大厂算法面试=>链表专题【链表删除】

冲击大厂算法面试>链表专题【链表删除】 本文学习目标或者巩固的知识点 学习如何删除链表中的某个节点 如何删除valk的节点如何删除倒数第n个节点 学习如何删除链表中的某些节点 涉及头节点问题如何解决 提前说明:算法题目来自力扣、牛客等等途径 &#x1f7e…

每日一学—由面试题“Redis 是否为单线程”引发的思考

文章目录 📋 前言🌰 举个例子🎯 什么是 Redis(知识点补充)🎯 Redis 中的多线程🎯 I/O 多线程🎯 Redis 中的多进程📝 结论🎯书籍推荐🔥参与方式 &a…

[更新]ARCGIS之土地耕地占补平衡、进出平衡系统报备坐标txt格式批量导出工具(定制开发版)

序言 之前开发的耕地占补平衡报备格式,现在之前的基础上集成了耕地进出平衡报备格式导出。 之前版本软件详见:软件介绍 一、软件简介 本软件是基于arcgis二次开发的工具(插件),需要授权后才能使用; 本软件…

【成像光谱遥感技术】如何使用ChatGPT应用自如

遥感技术主要通过卫星和飞机从远处观察和测量我们的环境,是理解和监测地球物理、化学和生物系统的基石。ChatGPT是由OpenAI开发的最先进的语言模型,在理解和生成人类语言方面表现出了非凡的能力。本课程重点介绍ChatGPT在遥感中的应用,人工智…

TongWEB(东方通),部署WEB前后端项目步骤

我的系统: 银河麒麟桌面系统V10(SP1)(兆芯) 环境需要搭建好,什么redis,数据库等 1.准备项目前端war包 (我后端项目本就是war部署,jar转war自行百度一下吧) 进入前端打包好的dist文件夹,创建一个文件夹 WEB-INF ,再在 WEB-INF 里创建一个 web.xml 文件,文件内容: <web-…