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

news2024/11/23 6:55:13

了解原型、原型链前需要先了解构造函数,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: null
        caller: null
        length: 0
        name: "DoSomething"
        prototype: {constructor: ƒ}
        [[FunctionLocation]]: VM1158:1
        [[Prototype]]: ƒ ()
    [[Prototype]]: Object
        constructor: ƒ 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 = 12
    this.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.coloradmin.cn/o/1010209.html

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

相关文章

idea2023全量方法debug

为什么要全量debug 刚上手项目或者研读开源项目源码的时候,我们对项目的结构,尤其是功能链路非常陌生,想要debug根本不知道断点打在哪,光靠文件名类名或者方法名去猜也不是个事。这时候只要配置一下全量debug模式,就能…

解决jupyter找不到虚拟环境的问题

解决jupyter找不到虚拟环境的问题 使用jupyter只能使用base环境,不能找到自己创建的虚拟环境。如下图,显示的默认的虚拟环境base的地址。 如何解决这个问题?需要两个步骤即可 1 . 在base环境中安装nb_conda_kernels这个库 activate base c…

ARM上市,冲击2023年美股最大IPO

KlipC报道:ARM于美国时间9月14日在纳斯达克挂牌上市。 KlipC的合伙人表示:“据媒体报道,位于发行价指导区间47至51美元的顶端,知情人士称,ARM曾考虑将IPO发行价确定为52美元,但随后又降低到了51美元&#x…

数学实验-数列与级数(Mathematica实现)

一、实验名称:数列与级数 二、实验环境:Mathematica 10.3软件 三、实验目的:本实验通过Mathematica 10.3软件编程演示Fibonacci数列、调和级数的函数图像及函数关系式,通过Mathematica 10.3软件发现数列与极限状态的性质&#x…

C++与JS实现WebSocket通信(C++服务端JS客户端)

天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。…

即时通讯如何做好安全防御

即时通讯工具该怎么样做好防御可以分为以下几点 1、清理安全漏洞定期定时的扫描服务器的网络节点,排查网络中的安全漏洞隐患,及时清理安全漏洞。避免这些漏洞被黑客利用,攻击服务器。 2、设置防火墙在服务器的骨干节点配置防火墙,…

前端面试要点

0914 JScript深拷贝和浅拷贝(js解构赋值算哪个?) 深拷贝和浅拷贝 回流和重绘 回流和重绘 webpack打包流程 Webpack打包 虚拟DOM 虚拟DOM git合并分支 git合并分支 CSS盒子模型 CSS盒子模型 0911 WebPack分包 webpack分包 ts泛型 ts泛型 优化…

java包的介绍

包 包就是文件夹,用来管理各种不同功能的 java 类,方便后续管理 包名的规则: 公司域名反写+包的作用,需要全部英文小写,见名知意。 导包的规则 使用同一个包中的类时,不需要导包。使用 java. Lang 包中的…

9月13日上课内容 第三章 ELK日志分析系统

本章结构 ELK日志分析系统简介 ELK日志分析系统分为 Elasticsearch Logstash Kibana 日志处理步骤 1.将日志进行集中化管理 2.将日志格式化(Logstash) 并输出到Elasticsearch 3.对格式化后的数据进行索引和存储 (Elasticsearch) 4.前端数据的展示(Kibana) Elasticsearch介…

【深度学习】 Python 和 NumPy 系列教程(十六):Matplotlib详解:2、3d绘图类型(2)3D散点图(3D Scatter Plot)

目录 一、前言 二、实验环境 三、Matplotlib详解 1、2d绘图类型 2、3d绘图类型 0. 设置中文字体 1. 线框图(Wireframe Plot) 2. 3D散点图(3D Scatter Plot) 一、前言 Python是一种高级编程语言,由Guido van Ross…

窗帘导轨轮小柱

实物 difference(){union(){cylinder(1, 3.5, 3.5, $fn360);cylinder(8, 2, 2, $fn360);}rotate([90,0,0])translate([0,6,-3])cylinder(6, 1, 1, $fn360); }

STM32WB55开发(3)----配置串口打印Debug调试信息

STM32WB55开发----3.配置串口打印Debug调试信息 概述硬件准备视频教学样品申请选择芯片型号配置时钟源配置时钟树RTC时钟配置查看开启STM32_WPAN条件配置HSEM配置IPCC配置RTC启动RF开启蓝牙开启串口调试配置蓝牙参数设置工程信息工程文件设置Keil工程配置代码配置结果演示 概述…

云原生服务无状态(Stateless)特性的实现

文章目录 为何要使用无状态服务?无状态服务的实现方法1. 会话状态外部化2. 负载均衡3. 自动伸缩4. 容器编排5. 数据存储6. 安全性 示例:使用Spring Boot实现无状态服务结论 🎉欢迎来到云计算技术应用专栏~云原生服务无状态(Statel…

2023年云南省职业院校技能大赛中职组“网络安全”赛项样题

2023年云南省职业院校技能大赛 中职组“网络安全”赛项样题 一、竞赛时间 总计:180分钟 二、竞赛阶段 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 A、B模块 A-1 登录安全加固 180分钟 200分 A-2 数据库加固 A-3 服务加固SSH\VSFTPD A-4 防火墙策…

数据结构与算法-堆树

一:引入 1.优先队列:大家还记得我们上节课讲的赫夫曼树,我使用了一个优先队列大大减轻了我们的开发任务,但是大家知道这个优先队列内部是如何实现的呢? 解决: 大顶堆,优先删除堆顶 2.如何实现一…

云原生Kubernetes:pod资源管理与配置

目录 一、理论 1.pod 2.pod容器分类 3.镜像拉取策略 4.pod 的重启策略 二、实验 1.Pod容器的分类 2.镜像拉取策略 三、问题 1.apiVersion 报错 2.pod v1版本资源未注册 3.格式错误 4.取行显示指定pod信息 四、总结 一、理论 1.pod (1) 概念 Pod是kubernetes中…

PWA及小程序在系统生态方面的支持对比

PWA代表“渐进式网络应用”(Progressive Web Application)。它是一种结合了网页和移动应用程序功能的技术概念。PWA旨在提供类似于原生应用程序的用户体验,包括离线访问、推送通知、后台同步等功能,同时又具有网页的优势&#xff…

Linux自动化构建项目工具——Makefile/makefile

目录 一,背景知识 二,makefile/Makefile的编写 1.创建makefile/Makefile文件 2.在Makefile文件里写编译代码 3.伪目标——.PHONY 1.伪目标的特点 2.怎样实现总是被执行 4.Makefile/makefile文件的不同编写风格 1.背景知识 2.改写 一,背…

kali安装volatility及插件mimikatz

1.准备工作 kali安装pip2 wget https://bootstrap.pypa.io/pip/2.7/get-pip.py python2 get-pip.py 查看pip2版本 python2 -m pip -v pip2安装升级 pip2 install --upgrade setuptools 安装2个库 pip2 install construct2.10.54 pip2 install pycryptodome 下载文件d…

python开发之个微的二次开发

简要描述: 自动通过群(url) 请求URL: http://域名地址/acceptUrl 请求方式: POST 请求头Headers: Content-Type:application/jsonAuthorization:Authorization值(…