js之原型及原型链

如果js没有构造函数

首先不考虑构造函数这个鬼东西,当他不存在。

这个时候,创建对象的方式就是

    <script type="text/javascript">var dog = {name: 'hachi',age: 3}</script>

然后在浏览器上观察该对象,可以看到该对象包含三个属性,age,name和[[Prototype]],这里的[[Prototype]]其实就是__proto__,咱也不知道为啥浏览器要显示[[Prototype]],但是确实这里只用使用dog.__proto__访问,好,下面就记住这个属性是__proto__就行了。这个属性的值是一个对象,该对象就是原型对象,类似于java中的父类的概念,只是类似。

接下来创建父对象animal,js没有父子的概念,这里应该称之为原型对象,然后让dog对象继承animal对象

    <script type="text/javascript">var dog = {name: 'hachi',age: 3}var animal = {shout: function (){}}dog.__proto__ = animal</script>

继续观察dog对象,红框表示,dog对象的原型对象是animal对象,即dog对象继承了animal对象,绿框表示,animal对象继承了Object对象。是的,任何对象,最终都会继承自Object对象。

这里的__proto__属性就表示了继承关系,例如a继承了b,那么a.__proto__ == b所以,上面这个例子,我们可以判断一下是否确实有该继承关系,如下图所示,确实存在继承关系。

为了更清楚的表示原型链的链条关系,下面创建myDog对象,继承dog对象

    <script type="text/javascript">var dog = {name: 'hachi',age: 3}var animal = {shout: function (){}}dog.__proto__ = animalvar myDog = {host: '张三'}myDog.__proto__ = dog</script>

可以看到myDog的原型对象的原型对象就是animal

到这里,原型链就变得超级简单了,其实就是由__proto__属性串起来的一系列对象。

但是,实际上我们不会生硬的设置__proto__属性,而是通过Object.reate函数来实现继承,如下代码所示

    <script type="text/javascript">var animal = {shout: function (){}}dog = Object.create(animal)dog.name = 'hachi'dog.age = 3myDog = Object.create(dog)myDog.host = '张三'</script>

但是,加入构造函数后,事情又变麻烦了

如果js有了构造函数

那就复杂一些了。上一章提到,可以使用obj = {a:1}的形式来产生对象,也可以通过obj = Object.create(obj_proto)来产生obj对象,并且该对象的原型对象是obj_proto,其实,还有第三种方式来产生对象,那就是通过构造函数。如下代码所示,构造函数可以粗糙的认为理解为java中的类,例如这里的Animal,Dog和MyDog,都是构造函数,使用new关键字可以生成实例对象,例如这里的dog = new Dog(),就生成了构造函数Dog的一个实例对象。一个构造函数可以生成许许多多的实例对象。

    <script type="text/javascript">var Animal = function (){this.shout = function (){}}var Dog = function (name,age){this.name = namethis.age = age}var MyDog = function (host){this.host = host}var animal = new Animal()var dog = new Dog('hachi', 3)var myDog = new MyDog('张三')</script>

在语句var dog = new Dog()的时候,实际上发生了什么呢?

  1. 创建一个空对象,作为将要返回的对象实例。
  2. 将这个空对象的原型,指向构造函数的prototype属性。也就是将Dog的prototype属性赋值给dog的__proto__属性
  3. 将这个空对象赋值给函数内部的this关键字。
  4. 开始执行构造函数内部的代码。例如这里的this.name = name, this.age=age

可以检查一下是否构造函数的prototype属性和实例对象的__proto__属性相等,如图所示,的确是这样。

接下来进一步研究prototype属性到底是个啥,如图所示,prototype属性其实就是一个对象,这里叫做原型对象,该原型对象包括两个属性,一个是constructor,一个是[[Prototype]]。那么问一个问题,这里的[[Prototype]]是prototype还是__proto__呢?哈哈,当然是__proto__了,因为这是一个原型对象,不是函数,只有函数才拥有prototype属性。(在js中,函数其实也是对象,但是这里为了讨论起来简单,我说的对象是指的非函数的对象,函数就直接叫做函数好了)。

如果该构造函数是自定义的函数且没有修改其prototype值,那么这里prototype对象的[[prototype]]属性其实就是Object对象,也就是说使用该构造函数生成的任何实例对象,其原型对象(即__proto__),都是Object。

constructor又是啥呢?其实就是构造函数本身,这个字段的作用就是为了让每个实例对象记住自己的构造函数。例如,构造函数Dog的原型对象的constructor属性,就是Dog,如下图所示,这里不要问为什么,反正就是这么规定的,规定prototype有个constructor属性,规定constructor属性的值就是构造函数本身。可以认为这个属性就是为了让实例对象记住自己的构造函数吧

那么如何通过构造函数来实现继承呢?

其实就是改变构造函数的prototype属性就好了,但是一定记得修改constructor属性。如下代码所示,Dog.prototype = animal将animal对象作为构造函数Dog的原型对象(prototype属性),Dog.prototype.constructor = Dog设置constructor属性的值为Dog;同理,MyDog.prototype = dog将dog对象作为构造函数MyDog的原型对象(prototype属性),MyDog.prototype.constructor = MyDog设置constructor属性的值为MyDog。

    <script type="text/javascript">var Animal = function (){this.shout = function (){}}var Dog = function (name,age){this.name = namethis.age = age}var MyDog = function (host){this.host = host}var animal = new Animal()Dog.prototype = animalDog.prototype.constructor = Dogvar dog = new Dog('hachi', 3)MyDog.prototype = dogMyDog.prototype.constructor = MyDogvar myDog = new MyDog('张三')</script>

可以检查一下继承关系是否构建好,如下图所示

其实也好理解,当我执行dog = new Dog()后,构造函数Dog的prototype属性赋值给dog的__proto__属性了,而Dog.prototype就是animal;当我执行myDog = new MyDog()后,构造函数MyDog的prototype属性赋值给myDog的__proto__属性了,而MyDog.prototype就是dog,所以继承关系就成立了。

总结起来就是,非函数对象都有一个__proto__属性,该属性指向其原型对象(也就是父对象),对象之间通过__proto__属性串联起来构成原型链,而对象可以通过构造函数产生,构造函数有一个prototype属性,使用构造函数生成示例对象后,prototype属性会赋值给实例对象的__proto__属性。

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

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

相关文章

Xcode 运行真机失败

错误提示&#xff1a; iPhone xxx is not available because it is unpaired. Pair with the device in the Xcode Devices Window, and respond to any pairing prompts on the device. 处理方法&#xff1a; 把Xcode关闭&#xff0c;手机断开数据线&#xff0c;打开终端&…

BIO、NIO、AIO、Netty从简单理解到使用

Java编程中BIO、NIO、AIO是三种不同的I/O&#xff08;输入/输出&#xff09;模型&#xff0c;它们代表了不同的I/O处理方式。 Netty就是基于Java的NIO&#xff08;New Input/Output&#xff09;类库编写的一个高性能、异步事件驱动的网络应用程序框架&#xff0c;用于快速开发可…

import模块到另一个文件夹报错:ModuleNotFoundError: No module named xxx

1. 问题 打开项目文件夹my_code&#xff0c;将bb.py的函数或者类import到aa.py中&#xff0c;然后运行aa.py文件&#xff0c;可能会报错ModuleNotFoundError: No module named xxx。 ‪E:\Desktop\my_code ├── a │ ├── train.sh │ └── aa.py └── b└── b…

怎么写C#命令行参数程序,及控制台带参数案例(程序完整源码)下载

C#命令行参数解析控制台带参数编写案例&#xff08;程序完整源码&#xff09;下载链接 https://download.csdn.net/download/luckyext/90434790 在CMD命令窗口&#xff0c;输入ping 、ipconfig等这样的命令&#xff0c;大家应该都知道&#xff0c;但很多同学可能不知道怎么写…

JAVA安全—手搓内存马

前言 最近在学这个内存马&#xff0c;就做一个记录&#xff0c;说实话这个内存马还是有点难度的。 什么是内存马 首先什么是内存马呢&#xff0c;顾名思义就是把木马打进内存中。传统的webshell一旦把文件删除就断开连接了&#xff0c;而Java内存马则不同&#xff0c;它将恶…

算法 并查集

目录 前言 一 并查集的思路 二 并查集的代码分析 三 实操我们的代码 四 并查集的代码优化 总结 前言 并查集主要是用来求解集合问题的&#xff0c;用来查找集合还有就是合并集合&#xff0c;可以把这个运用到最小生成树里面 一 并查集的思路 1 并查集的相关的操作…

vulnhub靶场之【digitalworld.local系列】的development靶机

前言 靶机&#xff1a;digitalworld.local-devt-improved&#xff0c;IP地址为192.168.10.10 攻击&#xff1a;kali&#xff0c;IP地址为192.168.10.6 kali采用VMware虚拟机&#xff0c;靶机选择使用VMware打开文件&#xff0c;都选择桥接网络 这里官方给的有两种方式&…

Stiring-PDF:开源免费的PDF文件处理软件

Stiring-PDF是一款开源免费且比较好用的PDF文件处理工具。 Stiring-PDF官网网址为&#xff1a;https://www.stiringpdf.com/。Stiring-PDF是一款专业的PDF文件处理工具&#xff0c;支持Windows和macOS操作系统&#xff1b;提供丰富的PDF编辑和转换功能&#xff0c;适用于日常工…

SpringCloud系列教程(十二):网关配置动态路由

除了token以外&#xff0c;还有一个很实用的功能就是把网关的路由配置放到nacos上&#xff0c;并且修改路由配置的时候&#xff0c;网关服务可以动态的更新&#xff0c;这样我们在调整网络配置的时候&#xff0c;就不用重启服务了。所以我们需要用到两个重要的类&#xff1a;Na…

R JSON 文件

R JSON 文件 引言 在当今的数据分析和处理领域&#xff0c;R语言作为一种功能强大的统计计算和图形展示工具&#xff0c;被广泛应用于各种数据分析任务中。随着大数据时代的到来&#xff0c;数据的格式和结构变得越来越多样化。JSON&#xff08;JavaScript Object Notation&a…

ES6 特性全面解析与应用实践

1、let let 关键字用来声明变量&#xff0c;使用let 声明的变量有几个特点&#xff1a; 1) 不允许重复声明 2) 块儿级作用域 3) 不存在变量提升 4) 不影响作用域链 5) 暂时性死区 6&#xff09;不与顶级对象挂钩 在代码块内&#xff0c;使用let命令声明变量之前&#x…

如何使用 Ollama 的 API 来生成聊天

如何使用 Ollama 的 API 来生成聊天 简介 生成聊天 生成聊天的示例 加载模型 卸载模型 简介 Ollama 提供了一个 RESTful API&#xff0c;允许开发者通过 HTTP 请求与 Ollama 服务进行交互。这个 API 覆盖了所有 Ollama 的核心功能&#xff0c;包括模型管理、运行和监控。本…

【学Rust写CAD】10 加法器

源码 // src/matrix/adder.rs/** 说明&#xff1a;连加计算中&#xff0c;为提高运行期效率&#xff0c;用该结构增加一个Const变量&#xff0c;方便单独合并所有Const类型&#xff0c;最后一步才有可能出现Const与Val的加法计算*/use std::ops::Add;use super::constant::{Co…

学到什么记什么(25.3.3)

Upload-labs 今日重新做了一下文件上传漏洞&#xff0c;这里第一题之前采用直接抓包改后缀名.jpg为.php&#xff0c;再写入一句话<?php phpinfo();?>然后放行&#xff0c;得到图片地址&#xff08;可复制&#xff09;&#xff0c;本来直接访问图片地址即可得到敏感信息…

el-table input textarea 文本域 自适应高度,切换分页滚动失效处理办法

场景&#xff1a; el-table 表格 需要 input类型是 textarea 高度是自适应&#xff0c;第一页数据都是单行数据 不会产生滚动条&#xff0c;但是第二页数据是多行数据 会产生滚动条&#xff0c; bug: 第一页切换到第二页 第二页滚动条无法展示 解决办法&#xff1a;直接修改样…

[杂学笔记] 封装、继承、多态,堆和栈的区别,堆和栈的区别 ,托管与非托管 ,c++的垃圾回收机制 , 实现一个单例模式 注意事项

文章目录 1.封装、继承、多态2. 堆和栈的区别3.指针和引用的区别4. 托管与非托管5. c的垃圾回收机制6. 实现一个单例模式 注意事项 1.封装、继承、多态 封装就是将数据和内部的方法封装到一个类中&#xff0c;对外隐藏内部实现细节&#xff0c;但是留下了公共接口提供给外部使…

【三维生成】StarGen:基于视频扩散模型的可扩展的时空自回归场景生成

标题&#xff1a;《StarGen: A Spatiotemporal Autoregression Framework with Video Diffusion Model for Scalable and Controllable Scene Generation》 项目&#xff1a;https://zju3dv.github.io/StarGen 来源&#xff1a;商汤科技、浙大CAD、Tetras.AI 文章目录 摘要一、…

【一个月备战蓝桥算法】递归与递推

字典序 在刷题和计算机科学领域&#xff0c;字典序&#xff08;Lexicographical order&#xff09;也称为词典序、字典顺序、字母序&#xff0c;是一种对序列元素进行排序的方式&#xff0c;它模仿了字典中单词的排序规则。下面从不同的数据类型来详细解释字典序&#xff1a; …

【Linux】【网络】UDP打洞-->不同子网下的客户端和服务器通信(成功版)

【Linux】【网络】UDP打洞–&#xff1e;不同子网下的客户端和服务器通信&#xff08;成功版&#xff09; 根据上个文章的分析 问题可能出现在代码逻辑上面 我这里重新查找资料怀疑&#xff1a; 1 NAT映射可能需要多次数据包的发送才能建立。 2 NAT映射保存时间太短&#xff…

SpaCy处理NLP的详细工作原理及工作原理框图

spaCy处理NLP的详细工作原理及工作原理框图 spaCy处理NLP的详细工作原理 spaCy是一个基于Python的开源自然语言处理&#xff08;NLP&#xff09;库&#xff0c;它提供了一系列高效且易用的工具&#xff0c;用于执行各种NLP任务&#xff0c;如文本预处理、文本解析、命名实体识…