新版原型和原型链详解,看完整个人都通透

了解原型、原型链前需要先了解构造函数,new操作符

构造函数

构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与 new 一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。

function Foo () {this.name = 'gl'this.fun = function () { console.log('eat')}
}const Foo = function (){this.xxx = xxx;
}const fooInstancing = new Foo();
const fooInstancing1 = new Foo();

构造函数的三大特点:

  • 构造函数的函数名的第一个字母通常大写。
  • 函数体内使用this关键字,代表所要生成的对象实例。
  • 生成对象的时候,必须使用new命令来调用构造函数。

new操作符

new命令的作用,就是执行一个构造函数,并且返回一个对象实例。使用new命令时,它后面的函数调用就不是正常的调用,而是依次执行下面的步骤。

  • 创建一个空对象,作为将要返回的对象实例
  • 将空对象的原型指向了构造函数的prototype属性。
  • 将空对象赋值给构造函数内部的this关键字。
  • 开始执行构造函数内部的代码。

也就是说,构造函数内部,this指向的是一个新生成的空对象,所有针对this的操作,都会发生在这个空对象上。
后续了解了原型之后我们再简单实现一个new

原型

在JavaScript没有class的时候,是使用构造函数来新建一个对象的,每一个构造函数的内部都有一个prototype属性,它的属性值是一个对象(prototype),这个对象包含了可以由该构造函数的所有实例共享的属性和方法。当使用构造函数新建一个对象后,在这个对象的内部将包含一个指针,这个指针指向构造函数的 prototype 属性对应的值,在 ES5 中这个指针(proto)被称为对象的原型。

一般来说不应该能够获取到这个值的,但是现在浏览器中都实现了 proto 属性来访问这个属性,但是最好不要使用这个属性,因为它快芭比Q了。ES5 中新增了一个Object.getPrototypeOf() 方法,可以通过这个方法来获取对象的原型。

结论

以上明白了 两点

  • prototype(原型,类型是对象,一般叫原型对象)是构造函数的
  • proto、Object.getPrototypeOf(obj)是获取对象的原型
  • prototype能干啥,共享~可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法
// 构造函数本质是函数
function DoSomething(){}
console.log( DoSomething.prototype );
// 两种写法
var DoSomething = function(){};
console.log( DoSomething.prototype );// ?
{constructor: ƒ}constructor: ƒ DoSomething()arguments: nullcaller: nulllength: 0name: "DoSomething"prototype: {constructor: ƒ}[[FunctionLocation]]: VM1158:1[[Prototype]]: ƒ ()[[Prototype]]: Objectconstructor: ƒ Object()hasOwnProperty: ƒ hasOwnProperty()isPrototypeOf: ƒ isPrototypeOf()propertyIsEnumerable: ƒ propertyIsEnumerable()toLocaleString: ƒ toLocaleString()toString: ƒ toString()valueOf: ƒ valueOf()__defineGetter__: ƒ __defineGetter__()__defineSetter__: ƒ __defineSetter__()__lookupGetter__: ƒ __lookupGetter__()__lookupSetter__: ƒ __lookupSetter__()__proto__: (...)get __proto__: ƒ __proto__()set __proto__: ƒ __proto__()

在这里插入图片描述
现在,我们可以添加一些属性到 doSomething 的原型上面,如下所示。

function DoSomething(){}
DoSomething.prototype.foo = "bar";
console.log( DoSomething.prototype ); // ?

在这里插入图片描述
然后,我们可以使用 new 运算符来在现在的这个原型基础之上,创建一个 doSomething 的实例。然后,就可以在这个对象上面添加一些属性。

function DoSomething(){}
DoSomething.prototype.foo = "bar"; 
var doSomeInstancing = new DoSomething();
doSomeInstancing.prop = "some value";
console.log( doSomeInstancing ); // ?

在这里插入图片描述

__proto__哪去了?

这时候会发现我们的__proto__哪去了,对象实例里面咋没有,凡事先看权威文档
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto

proto 的读取器 (getter) 暴露了一个对象的内部 [[Prototype]]
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain#%E7%BB%A7%E6%89%BF%E5%B1%9E%E6%80%A7
备注: 遵循 ECMAScript 标准,someObject.[[Prototype]] 符号是用于指向 someObject 的原型。从 ECMAScript 6 开始,[[Prototype]] 可以通过 Object.getPrototypeOf() 和 Object.setPrototypeOf() 访问器来访问。这个等同于 JavaScript 的非标准但许多浏览器实现的属性 proto。但它不应该与构造函数 func 的 prototype 属性相混淆。被构造函数创建的实例对象的 [[Prototype]] 指向 func 的 prototype 属性。Object.prototype 属性表示 Object 的原型对象。

那我们在试试Object.getPrototypeOf()和__proto__的关系

Object.getPrototypeOf(doSomeInstancing);
// ? 
doSomeInstancing.__proto__;// ? true or false
Object.getPrototypeOf(doSomeInstancing) === doSomeInstancing.__proto__;

某些浏览器内部还是支持__proto__的,例如谷歌,结论[[Prototype]] 就是 proto

在这里插入图片描述
下文还是用 大家熟知的__proto__说道

  • 回过头发现doSomeInstancing.__proto__和doSomething.prototype打印结果是一样的
    在这里插入图片描述
Object.getPrototypeOf(doSomeInstancing) === DoSomething.prototype;
// ?

在这里插入图片描述
所以 Object.getPrototypeOf(doSomeInstancing) === doSomeInstancing.proto === DoSomething.prototype

那什么是原型链呢

思考一个问题,如果访问doSomeInstancing里的一个属性,浏览器是怎么做的?

  1. 浏览器首先会查看doSomeInstancing 中是否存在这个属性
  2. 如果 doSomeInstancing 不包含属性信息,那么浏览器会在 doSomeInstancing__proto__ 中进行查找 (同 doSomething.prototype). 如属性在 doSomeInstancing__proto__中查找到,则使用 doSomeInstancing__proto__ 的属性。
  3. 否则,如果doSomeInstancing__proto__ 不具有该属性,则检查doSomeInstancing __proto____proto__ 是否具有该属性。默认情况下,任何函数的原型属性__proto__都是 window.Object.prototype. 因此,通过 doSomeInstancing __proto____proto__ ( 同 doSomething.prototype__proto__ (同 Object.prototype)) 来查找要搜索的属性。
  4. 如果属性不存在 doSomeInstancing __proto__ __proto__ 中,那么就会在doSomeInstancing 的 __proto__ __proto____proto__ 中查找。然而,这里存在个问题:doSomeInstancing __proto____proto____proto__ 其实不存在。因此,只有这样,在 __proto__ 的整个原型链被查看之后,这里没有更多的__proto__,浏览器断言该属性不存在,并给出属性值为undefined的结论。

找一个属性,要顺着原型__proto__一直找下去,这时候就会形成一个链路,这就是原型链的查找规则,而原型链就是这个规则本身。

但是要注意一点__proto__只是提供一种查找规则,是非标准的,实际指向的还是prototype 原型对象。实际开发中不推荐使用,推荐在prototype上操作。

constructor = 构造函数

**对象的原型( __ proto __ )和原型对象(prototype)**里面都有一个属性 constructor 属性 ,constructor 我们称为构造函数,因为它指回构造函数本身。

constructor 主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。

function DoSomething(eat){this.eat = eat;
}
var doSomeInstancing = new DoSomething('吃💩');console.log(DoSomething.prototype.constructor);
console.log(doSomeInstancing.__proto__.constructor);

在这里插入图片描述

FQA

proto、prototype、constructor关系

在这里插入图片描述

属性查找机制

  1. 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
  2. 如果没有就查找它的原型(也就是 proto 指向的 prototype 原型对象)。
  3. 如果还没有就查找原型对象的原型(Object的原型对象)。
  4. 依此类推一直找到 Object 为止(null)。
  5. proto 对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。

原型链的终点是什么?

由于Object是构造函数,原型链终点是Object.prototype.proto,而Object.prototype.proto=== null // true,所以,原型链的终点是null。原型链上的所有原型都是对象,所有的对象最终都是由Object构造的,而Object.prototype的下一级是Object.prototype.proto
在这里插入图片描述

代码输出

function DoSomething(){
}
DoSomething.prototype.foo = "bar";var doSomeInstancing = new DoSomething();doSomeInstancing.prop = "some value";// 下面代码输出
console.log("doSomeInstancing.prop:      " + doSomeInstancing.prop); // some value
console.log("doSomeInstancing.foo:       " + doSomeInstancing.foo); // bar
console.log("doSomething.prop:           " + DoSomething.prop); // undefined
console.log("doSomething.foo:            " + DoSomething.foo); // undefined
console.log("doSomething.prototype.prop: " + DoSomething.prototype.prop); // undefined
console.log("doSomething.prototype.foo:  " + DoSomething.prototype.foo); // bar
function Fn(){var a = 12this.getName = function(){console.log('private getName')}
}Fn.prototype.getName = function (){console.log('public getName')
}var fn = new Fn()
var fn1 = new Fn()
// 1,2
console.log(fn.a) // undefined
console.log(fn.getName()) // private getName
// 3,4,5
console.log(fn.getName === fn1.getName) // false 
console.log(fn.__proto__.getName === fn1.__proto__.getName) // true
console.log(fn.__proto__.getName === Fn.prototype.getName) // true
//6,7
console.log(fn.hasOwnProperty === Object.prototype.hasOwnProperty) // ture
console.log(fn.constructor === Fn) // ture 

new如何实现

  • 创建一个空对象,作为将要返回的对象实例。
  • 将空对象的原型指向了构造函数的prototype属性。
  • 将空对象赋值给构造函数内部的this关键字。
  • 开始执行构造函数内部的代码。
  • 返回此对象
function _new (fn, ...args) {// 不是一个函数if (typeof fn === 'function') {///创建一个空对象,作为将要返回的对象实例。const obj = new Object();// 将空对象的原型指向了构造函数的prototype属性。obj.__proto__ = fn.prototype;// 将空对象赋值给构造函数内部的this关键字const result = fn.apply(obj, args);return result instanceof Object ? result : obj; } else {throw TypeError('not a function');}
}

原型的应用

  • instanceof 如果A instanceof B。判断 B 的 prototype 属性指向的原型对象(B.prototype)是否在对象 A 的原型链上。
  • vue2的eventBus
  • 给某个对象加一个公用封装方法

finish,各位观众姥爷收藏➕关注,追好文不迷路💗

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

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

相关文章

手机提词器有哪些?简单介绍这一款

手机提词器有哪些?手机提词器在现代社会中越来越受欢迎,原因是它可以帮助人们提高演讲和朗读的效果。使用手机提词器可以让人们更加自信地面对演讲和朗读,不至于出现口误或读错字的情况。此外,手机提词器还可以帮助人们节省时间和…

认识Git的工作区、暂存区与版本库

使用 git init 命令在 gitcode 文件夹下创建如下图所示的Git仓库。现在思考这样一个问题:gitcode目录下创建的README文件可以直接被git管理和追踪吗? 答案是否定的,因为只有 Git 本地仓库中的文件才可以被版本控制。什么?难道当前…

TableConvert-免费在线表格转工具 让表格转换变得更容易

在线表格转工具TableConvert TableConvert 是一个基于web的免费且强大在线表格转换工具,它可以在 Excel、CSV、LaTeX 表格、HTML、JSON 数组、insert SQL、Markdown 表格 和 MediaWiki 表格等之间进行互相转换,也可以通过在线表格编辑器轻松的创建和生成…

【Spring使用三级缓存解决循环依赖的过程】

testService1和testService2相互依赖 当Spring创建testService1对象时,它会先从一级缓存中查找是否存在testService1的实例。如果缓存中不存在testService1实例,它将继续查找二级缓存中是否存在testService1。如果二级缓存中也不存在testService1实例&…

【SpringCloud微服务--Eureka服务注册中心】

SpringCloud微服务全家桶学习笔记【持续更新】 gitee仓库 内容:SpringCloud SpringCloud alibaba 技术栈:Java8mavengit,githubNginxRabbitMQSpringBoot2.0 微服务架构概述 微服务架构是一种架构模式,它提倡将单一应用程序划…

分布式多级缓存

例子(测试环境) 项目结构图 运行反向代理服务器也就是负责反向代理到三个nginx的nginx,该nignx也负责前端页面的跳转。 该nginx的conf为下: 突出位置就是该nginx需要反向代理的其他nginx的IP和端口。 Lua语法 linux安装Lua #安装lua环境 …

Uniapp中使用uQRCode二维码跳转小程序页面

下载插件 uQRCode官网地址 引入插件 文件如下 //--------------------------------------------------------------------- // github https://github.com/Sansnn/uQRCode //---------------------------------------------------------------------let uQRCode = {};(functio…

fabic如何将绘图原点移到画布中心

情况说明: fabic默认绘图原点为left:0,top:0 后端给我的内容是按照x,y返回的,需要将坐标系移到fabic画布的中心位置,找了下网上合适的砖,想一句命令直接设置,结果没有。…

改造el-dropdown ,实现多选效果,且当选项只剩下一个时,不允许取消

实现效果 实现代码 其中virtual-list是使用的插件,使得下拉数据多的时候,不会出现卡顿 正常不使用虚拟列表的时候可以这样写 <el-dropdown-menu slot"dropdown"><el-dropdown-item v-for"i in item.optionList" :key"i.id" :command&…

前端面试合集(三——浏览器)

浏览器的页面渲染 1.浏览器是如何渲染页面的&#xff1f;2. 什么是reflow(重排&#xff09;&#xff1f;3. 什么是repaint(重绘&#xff09;&#xff1f;4.为什么transform效率高&#xff1f; 1.浏览器是如何渲染页面的&#xff1f; 当浏览器的网络线程收到HTML文档之后&#…

AI已经改变游戏规则,新环境下如何用好这一新利器,提升开发团队的生产力

ChatGPT 4在今年3月刚刚发布&#xff0c;但在短短几个月内&#xff0c;它已经开始改变多个行业开发材料和资产的方式。 作为Perforce Software的首席技术官&#xff0c;我密切关注着新兴技术如何重新塑造和定义既定的工作步骤与流程。在我近30年的软件开发经验中&#xff0c;很…

【再识C进阶2(下)】详细介绍指针的进阶——利用冒泡排序算法模拟实现qsort函数,以及一下习题和指针笔试题

前言 &#x1f493;作者简介&#xff1a; 加油&#xff0c;旭杏&#xff0c;目前大二&#xff0c;正在学习C&#xff0c;数据结构等&#x1f440; &#x1f493;作者主页&#xff1a;加油&#xff0c;旭杏的主页&#x1f440; ⏩本文收录在&#xff1a;再识C进阶的专栏&#x1…

探究Nginx应用场景

1 静态资源 Nginx是一个流行的Web服务器和反向代理服务器&#xff0c;它可以用于托管静态资源。下面是一个简单的案例&#xff0c;展示了如何使用Nginx来提供静态资源。 假设你有一个名为example.com的域名&#xff0c;并且你希望使用Nginx来托管位于/var/www/html目录下的静…

CopyOnWriteArrayList源码分析

其中唯一的线程安全 List 实现就是 CopyOnWriteArrayList。 特点 由于读取操作不会对原有数据进行修改&#xff0c;因此&#xff0c;对于每次读取都进行加锁其实是一种资源浪费。相比之下&#xff0c;我们应该允许多个线程同时访问 List 的内部数据&#xff0c;毕竟对于读取操…

MeterSphere压测,出现HttpHostConnectException

现象&#xff1a;MeterSphere更换压力机后&#xff0c;压测出现出现HttpHostConnectException 解决方案&#xff1a; net.ipv4.tcp_tw_reuse默认是0或者2&#xff0c;更改为1 net.ipv4.tcp_tw_reuse&#xff0c;表示是否允许重新应用处于TIME-WAIT状态的socket用于新的TCP连…

时序分解 | MATLAB实现基于SSA奇异谱分析的信号分解分量可视化

时序分解 | MATLAB实现基于LMD局部均值分解的信号分解分量可视化 目录 时序分解 | MATLAB实现基于LMD局部均值分解的信号分解分量可视化效果一览基本介绍程序设计参考资料 效果一览 基本介绍 奇异谱分解奇异谱分析SSA 可直接替换txt数据运行 Matlab 1.包含3D分解效果图 频谱图等…

Linux UDP编程流程

文章目录 UDP编程流程UDP协议无连接的特点UDP协议数据报的特点 UDP编程流程 UDP 提供的是无连接、不可靠的、数据报服务。服务器端和客户端没有什么本质上的区别。编程流程如下&#xff1a; socket()用来创建套接字&#xff0c;使用 udp 协议时&#xff0c;选择数据报服务 SOC…

Spring基础(2w字---学习总结版)

目录 一、Spirng概括 1、什么是Spring 2、什么是容器 3、什么是IoC 4、模拟实现IoC 4.1、传统的对象创建开发 5、理解IoC容器 6、DI概括 二、创建Spring项目 1、创建spring项目 2、Bean对象 2.1、创建Bean对象 2.2、存储Bean对象&#xff08;将Bean对象注册到容器…

IMU+摄像头实现无标记运动捕捉

惯性传感和计算机视觉的进步为在临床和自然环境中获得精准数据带来了新可能。然而在临床应用时需要仔细地将传感器与身体对齐&#xff0c;这减慢了数据收集过程。 随着无标记运动捕捉的发展&#xff0c;研究者们提出了一个新的深度学习模型&#xff0c;利用来自视觉、惯性传感…

金和OA GetSqlData.aspx 远程命令执行漏洞

一、漏洞简介 金和OA协同办公管理系统C6软件共有20多个应用模块,160多个应用子模块,涉及的企业管理业务包括协同办公管理、人力资源管理、项目管理、客户关系管理、企业目标管理、费用管理等多个业务范围,从功能型的协同办公平台上升到管理型协同管理平台,并不断的更新完善…