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

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

        在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.rhkb.cn/news/262203.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

grid新建主从一对多

目录 总结一、步骤前端1.第一步-编写tabs的modelBody2.第二步编辑表扩展js 后端--重写表的add和Update方法1.第一步 总结 编写tabs的modelBody后编辑表扩展js在重写后端partial的Service 一、步骤 前端 1.第一步-编写tabs的modelBody 复制下面代码该改的改 <template&…

Leetcoder Day17| 二叉树 part06

语言&#xff1a;Java/C 654.最大二叉树 给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下&#xff1a; 二叉树的根是数组中的最大元素。左子树是通过数组中最大值左边部分构造出的最大二叉树。右子树是通过数组中最大值右边部分构造出的最大二叉树。 …

PLC设置网口通讯的原因

PLC设置网口通讯功能&#xff0c;是现场总线向工业以太网的迈进&#xff0c;更是为了在网口之上构建更为庞大、开放的大一统的生态系统。 有了以太网&#xff0c;特别是TCP/IP协议后&#xff0c;全员工控人的日常工作更为便利了。 主要体现在以下4点&#xff1a; 1.再也不需要…

linux逻辑卷/dev/mapper/centos-root扩容增加空间

centos7中/dev/mapper/centos-root扩容 问题文件系统根目录&#xff0c;/dev/mapper/centos-root空间满了&#xff0c;导致k8s不停重启 1.查看磁盘情况 df -h #查看最大占用目录 du -h -x --max-depth12.查看磁盘信息 fdisk -l3.查看磁盘分区层级 lsblk可见剩余空闲60G空间…

Visual Paradigm 工具使用思考

大型项目的管理与实施&#xff0c;需要有高效的管理工具&#xff0c;VP算是不错的&#xff0c;美中不足是界面太死板&#xff0c;使用不便利&#xff0c;对于小型项目按照这个模式来&#xff0c;相当麻烦。 当然肯定会有人觉得不错&#xff0c;需要的&#xff0c;联系我

nacos集群部署

一 部署mysql 根据下面文章 docker安装mysql和数据挂载_docker run -p 3306:3306 -name mysql -v /mydata/mys-CSDN博客 建议&#xff1a;使用Mysql5.7&#xff0c;之前尝试使用Mysql8.0&#xff0c;会报错 经过查询&#xff1a; 报错原因 可能是数据库版本过高 二 部署na…

蓝桥杯备赛系列——倒计时50天!

蓝桥杯备赛系列 倒计时50天&#xff01; 前缀和和差分 知识点 **前缀和数组&#xff1a;**假设原数组用a[i]表示&#xff0c;前缀和数组用sum[i]表示&#xff0c;那么sum[i]表示的是原数组前i项之和&#xff0c;注意一般用前缀和数组时&#xff0c;原数组a[i]的有效下标是从…

【安卓基础3】Activity(一)

&#x1f3c6;作者简介&#xff1a;|康有为| &#xff0c;大四在读&#xff0c;目前在小米安卓实习&#xff0c;毕业入职 &#x1f3c6;本文收录于 安卓学习大全&#xff0c;欢迎关注 &#x1f3c6;安卓学习资料推荐&#xff1a; 视频&#xff1a;b站搜动脑学院 视频链接 &…

设置主从复制时发生报错Could not find first log file name in binary log index file‘;解决方案

如图所示&#xff0c;slave_io_runnind:no,slave_sql_running:yes 此时&#xff0c;主从配置错误&#xff0c;我们可以查看Last_IO_Error:来查看报错信息 此时&#xff0c;我们需要停止从服务器的主从服务&#xff0c; mysql> stop slave; Query OK, 0 rows affected, 1 w…

回显服务器的制作方法

文章目录 客户端和服务器TCP和UDP的特点UDP socket api的使用DatagramSocketDatagramPacketInetSocketAddress API 做一个简单的回显服务器UDP版本的回显服务器TCP版本的回显服务器 客户端和服务器 在网络中&#xff0c;主动发起通信的一方是客户端&#xff0c;被动接受的这一方…

1. 浏览器跨 Tab 窗口通信原理

浏览器跨 Tab 窗口通信原理 ![01 所谓多窗口下进行互相通信&#xff0c;是指在浏览器中&#xff0c;不同窗口&#xff08;包括不同标签页、不同浏览器窗口甚至不同浏览器实例&#xff09;之间进行数据传输和通信的能力。 当然&#xff0c;本文我们探讨的是纯前端的跨 Tab 页…

Web 前端 UI 框架Bootstrap简介与基本使用

Bootstrap 是一个流行的前端 UI 框架&#xff0c;用于快速开发响应式和移动设备优先的网页。它由 Twitter 的设计师和工程师开发&#xff0c;现在由一群志愿者维护。Bootstrap 提供了一套丰富的 HTML、CSS 和 JavaScript 组件&#xff0c;可以帮助开发者轻松地构建和定制网页和…

Springboot医院信息管理系统源码 带电子病历和LIS Saas应用+前后端分离+B/S架构

目录 系统特点 技术架构 系统功能 1、 标准数据维护 2、 收费&#xff08;门诊/住院&#xff09;系统 3、 药剂管理系统 4、 医生工作站系统 5、 护士工作站系统 6、电子病历系统 系统优点 云HIS系统简介 云HIS系统功能模块 门急诊挂号管理 门诊收费管理 门诊医…

Gitee教程2(完整流程)

1.配置git git config --global user.name "用户名" git config --global user.email "密码" 如何获取&#xff1f; gitee右上角加号点击新建仓库&#xff0c;仓库名随便起一个就行 找到这条命令&#xff0c;把这两句一个一个复制到vscode终端就行 2.创建g…

RabbitMQ的安装与使用

RabbitMQ的安装与使用 介绍一、RabbitMQ的安装1 查找镜像2 拉取镜像3 查看镜像4 创建容器5 查看容器6 访问测试 二、RabbitMQ的使用1 创建项目2 配置文件3 队列配置文件4 消费者5 生产者6 测试 三、交换器四、普通队列Demo五、死信队列Demo1 介绍2 示例2.1 配置2.2 生产者2.3 消…

【非常详细!】QT基础【二万字长文】

&#x1f308;个人主页&#xff1a;godspeed_lucip &#x1f525; 系列专栏&#xff1a;QT从基础到进阶 1 QMake2 Qt中三个窗口部件的区别2.1 QMainWindow2.2 QWidget2.3 QDialog 3 Visual Studio的QT项目与QtCreater项目相互转换3.1 QtCreater项目转VS项目3.2 VS项目转QtCreat…

百度地图海量点方案趟坑记录(百度地图GL版 + MapVGL + vue3 + ts)

核心需求描述 不同层级有不同的海量图标展示底层海量图标需要展示文字拖动、放大缩小都需要重新请求数据并展示固定地图中心点&#xff08;拖动、放大缩小&#xff0c;中心点始终在地图中心&#xff09; 示例图片&#xff1a;&#xff08;某些图片涉及公司数据&#xff0c;就未…

苍穹外卖Day02——总结2

前期文章 文章标题地址苍穹外卖Day01——总结1https://blog.csdn.net/qq_43751200/article/details/135466359?spm1001.2014.3001.5501苍穹外卖Day01——解决总结1中存在的问题https://lushimeng.blog.csdn.net/article/details/135473412 总结2 前期文章1. 新增员工模块1.1 …

初阶数据结构之---顺序表和链表(C语言)

引言-线性表 线性表&#xff1a; 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构。线性表在逻辑上是线性结构&#xff0c;也就是说是连续的一条直线。但在物理上并不一定是连续的。线性表在物理上…

Django学习笔记-创建第一个django项目

1.创建一个虚拟环境的python项目 2.点击解释器设置 3.安装django包 4.终端选择Command Prompt 5.创建django项目运行django-admin startproject demo01(自命名) 6.修改连接数据库为mysql 7.修改语言(中国汉语)和时区(亚洲上海)USE_TZ改为False,否则时区不生效 8.修改TEMPLA…