es5的实例__proto__(原型链) prototype(原型对象) {constructor:构造函数}

现在看这张图开始变得云里雾里,所以简单回顾一下 prototype 的基本内容,能够基本读懂这张图的脉络。
在这里插入图片描述
先介绍一个基本概念:

function Person() {}Person.prototype.name = 'KK';let person1 = new Person();

在上面的例子中,

Person 叫做构造函数(函数被进行构造调用,为下文方便,称之为构造函数
Person.prototype 叫做 Person 的原型对象
person1 又称之为实例
TL;DR.
构造函数默认会包含一个 prototype 属性,其值指向原型对象,即 Parent.prototype = Parent.prototype
默认情况下,构造函数.原型对象.constructor = 构造函数 Parent.prototype.constructor = Parent 。但如果有明确改写,则未必是这种指向。
实例.proto = 构造函数.原型对象 person.proto = Parent.prototype
实例和构造函数的原型对象之间有直接联系,但是实例和构造函数之间没有直接联系。

原型对象

在创建函数时, Function 的构造器产生函数对象时为其绑定的一个存放继承特征的对象属性prototype 。 Function 的构造器产生函数对象时,会运行类似的代码:

this.prototype = { constructor : this}

这个属性 prototype 的值是默认只会获得一个包含 constructor 属性的对象,其余的方法都继承自 Object。这个对象就是通过调用构造函数创建的对象的原型。使用原型对象 prototype 的好处就是,在它上面定义的属性和方法可以被对象的实例所共享。 原本在构造函数中给对象实例赋的值,可以直接赋值给它们的原型。

function Parent() {}// 定义在 Parent.prototype 的值,可以被该对象实例共享。
Parent.prototype.name = 'KK';Parent.prototype.sayHi = function () {console.log('hi,', this.name);
};// Parent 对象的实例 person1, person2
let person1 = new Parent();
let person2 = new Parent();person1.sayHi(); // hi, KK
person2.sayHi(); // hi, KK

而 prototype 里包含的 constructor 指向了与之关联的构造函数。如下图所示,我们可以看到 Parent 的 prototype 指向了 Parent.prototype (看起来是句废话),而 Parent.prototype 内的 constructor 指向了 Parent 。换言之,构造函数和构造函数原型对象的 constructor 之间形成了循环引用。

在这里插入图片描述
在这里插入图片描述

proto | [[prototype]]

开始之前,让我们来回顾一下 new 操作符, Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的 proto。 通过这一步,实现了实例对象和构造函数 func 的原型对象进行链接,从而方便访问定义在原型对象上的方法或者属性。

function myNew(func, ...args) {// 1. 在内存中创建一个新对象const obj = Object.create()// 2. 在这个新对象内部的 [[ prototype ]] 特性被赋值为构造函数的 prototype 属性obj.__proto__ = func.prototype// 3. 构造函数内部的 this 被赋值为这个新对象// 执行构造函数内部代码let result = func.apply(obj, args)// 4. 如果构造函数返回一个非空对象,则返回该对象,都则则返回刚刚创建的新对象return result instanceof Object ? result : obj
}

其中,第1.2步,可以合并起来变成 const obj = Object.create(func.prototype) , Object.create() 方法会创建一个新对象,并把新对象的 proto 关联到指定的对象上。

回顾完 new 操作符,接着我们刚刚的例子, person1 和 person2 ,它们是由 Parent 创建的对象实例。每一次我们通过构造函数创建一个新实例的时候,实例会通过 proto 链接到构造函数的原型对象上面,及下图的第一个部分。

function Parent() {}// Parent 对象的实例 person1, person2
let person1 = new Parent();
let person2 = new Parent();

但是,我们从图中可以看到,对象实例 person 的 proto 直接指向了其构造函数的原型对象 Parent.prototype ,实例和原型对象之间,其实是一个引用的关系,并不是重新保存了一个原型对象的副本。同时,我们也可以看到,对象实例和构造函数并没有发生直接的联系。

在这里插入图片描述

console.log(person1.__proto__ === Parent.prototype); // true
console.log(person1.__proto__.constructor === Parent); // trueconsole.log(person1 instanceof Parent)

原型链查找

我们经常会提起一个沿着原型链进行查找,但是怎么算是原型链查找呢?如何进行原型链查找呢?

对于对象默认的 get 操作,会先开始从对象实例开始寻找,如果发现则返回给定的对象属性值,否则搜索将进入原型对象,在原型对象上开始寻找,再返回对应的值。如上一节的图示,我们可以看到当我们输入 person.name 那么他首先会进入 person 中,进行寻找。当寻找无果后,再进入 Parent.prototype 进行寻找。这也就是原型可以再多个对象实例间共享属性和方法的原理了。

function Parent() {name: 'Parent';
}Parent.prototype.nickname = 'KK';Parent.prototype.sayHi = function () {console.log(this.nickname);
};let person1 = new Parent();person1.nickname = 'person1';
person1.sayHi();console.log(person1.nickname); // person1 

如果在实例对象上添加一个和原型对象同名的属性,那么就会在实例上创建这个属性值,这个在实例对象上的同名属性就会遮蔽原型对象上原有的属性。所谓遮蔽,只是因为会屏蔽对原型对象上同名属性的访问,但是并不会修改。只有使用 delete 才能完全删除实例上的这个属性,恢复对原型对象同名属性的访问。 否则,即使将实例上的同名属性修改为 null ,也无法恢复它和原型对象同名属性的联系。

属性’覆写’ or 遮蔽 Shadow
当然,这个对象属性的设置也有一定的规则,所谓的屏蔽也比我们想象的复杂。 我们可以来分析一下在实例对象上进行同名属性 set 动作的过程,这个会分为几个情况:

当原型对象上,不存在该属性时,直接设置即可;
当原型对象上,存在该属性:
仅为普通数据访问属性,且没有设置 writable:false ,则会在实例对象上,添加一个同名属性,遮蔽对原来原型对象上同名属性的访问;
当设置了 writable:false,标记该属性为只读属性(read-only),那么给实例对象设置同名属性的操作将会被拦截,这个复制操作在严格模式会报错,在非严格模式下会默认忽略。总言之,无法对原型对象的同名属性产生遮蔽的效果。
Attention: 看起来这个只要原型对象上存在 read-only 的属性,则无法进行同名属性赋值有点令人疑惑。但是这个限制只存在于 = 的复制操作中,如果直接使用 Object.defineProperty() 则不会受到影响,还是可以直接给实例对象进行复制操作。

如果原型对象的同名属性被设置了 setter , 那么这个 setter 会被调用,这个设置同名属性的动作会做用于这个 setter ,同样无法对原型对象的同名属性产生遮蔽的效果。

in 和 hasOwnProperty 属性来源

假定我们有这么一段代码,我们可以看到我们通过普通的复制方式,遮蔽了 person 原型对象上的 nickname 属性。不管这个属性是在实例上还是在原型上,只要在对象属性可以通过对象访问时,使用 in 操作符,操作结果都会返回 true。

function Parent() {nickname: 'Parent';
}Parent.prototype.nickname = 'KK';let person1 = new Parent();
let person2 = new Parent();person1.nickname = 'person1';console.log('nickname' in person1); // true
console.log('nickname' in person2); // true

我们知道,对于属性访问,即对属性的 get 操作,是会沿着原型链上进行查找,直至找到或者到根对象上。所以,如果需要判断这个属性到底是在实例对象上还是原型对象上,可使用 hasOwnProperty() 方法,这个方法可以用来确定某个属性在实例上还是在原型对象上,当且仅当这个属性是在调用它的对象上时,返回 true ,如:

console.log(person1.hasOwnProperty('nickname')); // true
console.log(person2.hasOwnProperty('nickname')); // false

结合上面提到的 in 和 hasOwnProperty ,那么我们只想要原型对象上的属性又要怎么进行判断呢? 可以创造一个 hasPrototypeProperty 方法,结合 in 和 hasOwnProperty 各自的特点:

function hasPrototypeProperty(obj, propertyKey) {return !obj.hasOwnProperty(propertyKey) && propertyKey in obj;
}

属性获取

当涉及到了遍历,在对象属性遍历中,我们可以用 for-in , Object.keys() ,单这二者也是有一定的区别的。

for-in
在 for 里面使用 in 操作符,可通过对象访问且可枚举的属性都会被返回,包括实例属性以及原型对象上的属性。当我们设置了 Enumberable:false 时,那么这个对象就变成了一个不可枚举属性,他才不会在循环中返回。需要注意一点,当我们在实例上定义一个同名属性去遮蔽原型对象一个不可枚举的属性时,这个在实例对象上没有被显式定义为不可枚举的同名属性,可以被返回,即不可枚举属性不会影响到实例属性。

Object.keys()
若想要获得对象上所有可枚举属性时,可以用 Object.keys() 的方法,这个方法接受对象作为参数,返回该对象上所有可枚举属性名数组,而不会包含其原型对象上属性。

可以把这个方法理解为 for-in + hasOwnProperty 的结合,只返回在对象上的属性,而不会沿着原型链进行查找。

Object.getOwnPropertyNames()
如果想列出所有实例属性,无论是否可以枚举,可以使用Object.getOwnPropertyNames()。但是需注意,这个方法的返回结果会包含了不可枚举属性。当我们对原型对象使用这个方法时,该方法会把 constructor 给返回回来。

在 ES6 出现 Symbol 的符号类型之后,相应也会出现一个 Object.getOwnPropertySymbols(),这个方法其实与 getOwnPropertyNames 类似,只是这个方法针对 Symbol 这种符号类型而已。

对于这几个方法,for-in , Object.keys() 的枚举顺序返回是不确定的,具体实现是取决于对应的 JavaScript 引擎的实现,而用 Object.getOwnPropertyNames() or Object.assign() 则是确定的,结果的返回会依据枚举数值键-插入的顺序枚举字符串-符号键。在 YDKJS 里有例子,我稍微拓展一下,可能看得更清晰一些:

let sym1 = Symbol('sym1')
let sym2 = Symbol('sym2')
let sym3 = Symbol('sym3')let obj = {1: 1,[sym3]: 'sym3',[sym1]: 'sym1',second: 'second',first: 'first',0: 0
}obj[sym2] = 'sym2'
obj[3] = 3
obj.forth = 'forth'
obj.third = 'third'
obj[2] = 2console.log(Object.getOwnPropertyNames(obj)) // [ '0', '1', '2', '3', 'second', 'first', 'forth', 'third' ]
console.log(Object.getOwnPropertySymbols(obj)) //  [ Symbol(sym3), Symbol(sym1), Symbol(sym2) ]

prototype 判断方法

Object.getPrototypeOf()
Object.getPrototypeOf() ****方法返回指定对象的原型(内部 [[Prototype]] 属性的值)。具体用法为:

Object.getPrototypeOf(object)Object.getPrototypeOf(person1); // { name: 'KK', sayHi: [Function (anonymous)] } Object.getPrototypeOf(person1) === Parent; // true

Object.prototype.isPrototypeOf()
Object.prototype.isPrototypeOf() 方法用于测试一个对象是否存在于另一个对象的原型链上。虽然并不是所有的方法都实现了对外暴露 proto ,但是我们可以通过 isPrototypeOf() 来确定 Parent.prototype 和 person 之间的关系,而因为 person 中有链接指向了 Person.prototype 。 所以结果返回为 true 。

Parent.prototype.isPrototypeOf(person1); // true

instanceof
instanceof 用于实例的原型链上是否包含了某个构造函数的 prototype 。具体用法为, instance instanceof constructionFunc 。 但是,注意,当我们显式改变了某个实例的 prototype 时,这个方法恐不适用。

person1 instanceof Parent ; // true

本文简单介绍了关于 prototype 的一些相关内容,对于文章开头的图片,现在我们回过头来看,虽然还是觉得反应会比较慢,但是仔细思考之后应该可以看懂整张图的链路和脉络。 简单休息一下,好好消化一下内容,接下来就要开始新的文章章节,原型链继承。

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

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

相关文章

腾讯混元助手使用指南

一、腾讯混元助手简介 腾讯混元助手是什么? 腾讯混元助手是由腾讯研发的大语言模型的平台产品,具备跨领域知识和自然语言理解能力,实现基于人机自然语言对话的方式,理解用户指令并执行任务,帮助用户实现人获取信息&am…

SpringBoot整合Websocket(Java websocket怎么使用)

目录 1 Websocket是什么2 Websocket可以做什么3 Springboot整合Websocket3.1 服务端3.2 客户端 1 Websocket是什么 WebSocket 是一种基于 TCP 协议的全双工通信协议,可以在浏览器和服务器之间建立实时、双向的数据通信。可以用于在线聊天、在线游戏、实时数据展示等…

算法通关村第十七关:青铜挑战-贪心其实很简单

青铜挑战-贪心其实很简单 1. 难以解释的贪心算法 贪心学习法则:直接做题,不考虑贪不贪心 贪心(贪婪)算法 是指在问题尽心求解时,在每一步选择中都采取最好或者最优(最有利)的选择,从而希望能够导致结果最…

【爬虫笔记】Python爬虫简单运用爬取代理IP

一、前言 近些年来,网络上的爬虫越来越多,很多网站都针对爬虫进行了限制,封禁了一些不规则的请求。为了实现正常的网络爬虫任务,爬虫常用代理IP来隐藏自己的真实IP,避免被服务器封禁。本文将介绍如何使用Python爬虫来…

百度智能云千帆大模型丨未来人手必备的代码助手

文章目录 1. 前言2. 千帆大模型平台3. 十分友好的功能4. comate代码助手5. 总结 1. 前言 我之前给大家推荐过Poe这个网站,它用的人比较少,但一旦接触后会发现它其实挺强大的。 因为它是一个可以同时支持好几个大模型的在线聚合平台。常用的GPT4&#x…

基于阻塞队列的生产消费模型

目录 一、线程同步 1.生产消费模型(或生产者消费者模型) 2.认识同步 (1)生产消费模型中的同步 (2)生产者消费者模型的特点 二、条件变量 1.认识条件变量 2.条件变量的使用 3.代码改造 三、基于阻…

uniapp移动端h5设计稿还原

思路 动态设置html的font-size大小 实现步骤 先创建一个public.css文件,设置初始的font-size大小 /* 注意这样写 只能使用css文件, scss 是不支持的, setProperty 只适用于原生css上 */ html {--gobal-font-size: 0.45px; } .gobal-font-size {font-size: var(--g…

leetcode 655. 输出二叉树(java)

输出二叉树 题目描述代码演示 题目描述 难度 - 中等 leetcode 655. 输出二叉树 给你一棵二叉树的根节点 root ,请你构造一个下标从 0 开始、大小为 m x n 的字符串矩阵 res ,用以表示树的 格式化布局 。构造此格式化布局矩阵需要遵循以下规则&#xff1a…

Python接口自动化封装导出excel方法和读写excel数据

一、首先需要思考,我们在页面导出excel,用python导出如何写入文件的 封装前需要确认python导出excel接口返回的是一个什么样的数据类型 如下:我们先看下不对返回结果做处理,直接接收数据类型是一个对象,无法获取返回值…

IOC和注解

想要学好spring,必须时时刻刻想着,spring的本质就是一个容器,放java对象的容器,java对象在spring容器中也叫做bean对象。 文章目录 一、spring介绍1、什么是框架2、框架的作用![在这里插入图片描述](https://img-blog.csdnimg.cn…

这几招真管用!找回丢失的iPhone的好方法!

你昂贵的iPhone不见了。它丢了吗?它被偷了吗?如果你把iPhone弄丢了,你可以从各种其他来源找到它,包括iPad、Mac、iCloud和Apple Watch。 你可以使用iCloud网站上的苹果“查找我的”应用程序、你的任何其他苹果设备或你家人注册的设备来追踪它。或者从“查找我的”应用程序…

Java基础知识点汇总

一、Java基础知识点整体框架 详细知识点见链接资源,注:框架是用Xmind App完成,查看需下载。 二、基础知识各部分概况 2.1 认识Java 2.2 数据类型和变量 2.3 运算符 2.4 程序逻辑控制 2.5 方法的使用 2.6 数组的定义和使用 2.7 类和对象 2.8 …

移植STM32官方加密库STM32Cryptographic

感谢这位博主,文章具有很高的参考价值: STM32F1做RSA,AES数据加解密,MD5信息摘要处理_我以为我爱了的博客-CSDN博客 概述 ST官方在很多年前就推出了自己的加密库,配合ST芯片用起来非常方便,支持ST的所有…

借助CIFAR10模型结构理解卷积神经网络及Sequential的使用

CIFAR10模型搭建 CIFAR10模型结构 0. input : 332x32,3通道32x32的图片 --> 特征图(Feature maps) : 3232x32即经过32个35x5的卷积层,输出尺寸没有变化(有x个特征图即有x个卷积核。卷积核的通道数与输入的通道数相等,即35x5&am…

SpringCloud(十)——ElasticSearch简单了解(一)初识ElasticSearch和RestClient

文章目录 1. 初始ElasticSearch1.1 ElasticSearch介绍1.2 安装并运行ElasticSearch1.3 运行kibana1.4 安装IK分词器 2. 操作索引库和文档2.1 mapping属性2.2 创建索引库2.3 对索引库的查、删、改2.4 操作文档 3. RestClient3.1 初始化RestClient3.2 操作索引库3.3 操作文档 1. …

网络技术二十二:NATPPP

NAT 转换流程 产生背景 定义 分类 常用命令 PPP PPP会话建立过程 认证 PPP会话流程

第 3 章 栈和队列(循环队列的顺序存储结构实现)

1. 背景说明 和顺序栈相类似,在队列的顺序存储结构中,除了用一组地址连续的存储单元依次存放从队列头到队列尾的元素之外, 尚需附设两个指针 front 和 rear 分别指示队列头元素及队列尾元素的位置。约定:初始化建空队列时&#x…

qt nodeeditor编译安装

目录 1. 下载源码 2. Qt creator编译源码 2.1 编译debug模式 (MinGW) 2.2 编译release模式 (MinGW) 1. 下载源码 https://github.com/paceholder/nodeeditor/archive/refs/tags/3.0.10.zip 2. Qt creator编译源码 解压文件…

Java 数据库改了一个字段, 前端传值后端接收为null问题解决

前端传值后端为null的原因可能有很多种,我遇到一个问题是,数据库修改了一个字段,前端传值了,但是后台一直接收为null值, 原因排查: 1、字段没有匹配上,数据库字段和前端字段传值不一致 2、大…

c语言 4.0

💂 个人主页: 程序员爱摸鱼🤟 版权: 本文由【程序员爱摸鱼】原创、在CSDN首发、需要转载请联系博主💬 如果文章对你有帮助、欢迎关注点赞收藏(一键三连)哦💅 想寻找共同成长的小伙伴,可以互粉哦 💬文章目录…