当前位置: 首页 > news >正文

js对象和原型、原型链的关系

JS的原型、原型链一直是比较难理解的内容,不少初学者甚至有一定经验的老鸟都不一定能完全说清楚,更多的"很可能"是一知半解,而这部分内容又是JS的核心内容,想要技术进阶的话肯定不能对这个概念一知半解,碰到问题靠“猜”,却不理解它的规则!

prototype

只有函数有prototype属性

let a = {}
let b = function () { }
console.log(a.prototype) // undefined
console.log(b.prototype) // { constructor: function(){...} }

Object.prototype怎么解释?

其实Object是一个全局对象,也是一个构造函数,以及其他基本类型的全局对象也都是构造函数:

function outTypeName(data, type) {let typeName =  Object.prototype.toString.call(data)console.log(typeName)
}
outTypeName(Object) //[object Function]
outTypeName(String) // [object Function]
outTypeName(Number) // [object Function]

为什么只有函数有prototype属性

JS通过new来生成对象,但是仅靠构造函数,每次生成的对象都不一样。

有时候需要在两个对象之间共享属性,由于JS在设计之初没有类的概念,所以JS使用函数的prototype来处理这部分需要被共享的属性,通过函数的prototype来模拟类:

当创建一个函数时,JS会自动为函数添加prototype属性,值是一个有constructor的对象。

以下是共享属性prototype的栗子:

function People(name) {this.name = name
}
People.prototype.age = 23 // 岁数
// 创建两个实例
let People1 = new People('OBKoro1')
let People2 = new People('扣肉')
People.prototype.age = 24 // 长大了一岁
console.log(People1.age, People2.age) // 24 24

为什么People1People2可以访问到People.prototype.age

原因是:People1People2的原型是People.prototype,答案在下方的:构造函数是什么以及它做了什么。

原型链

__proto__Object.getPrototypeOf(target): 对象的原型

__proto__是对象实例和它的构造函数之间建立的链接,它的值是:构造函数的`prototype。

也就是说:__proto__的值是它所对应的原型对象,是某个函数的prototype

Object.getPrototypeOf(target)全等于__proto__

它是ES6的标准,兼容IE9,主流浏览器也都支持,MDN,本文将以Object.getPrototypeOf(target)指代__proto__

不要再使用__proto__:

本段摘自阮一峰-ES6入门,具体解析请点击链接查看

  1. __proto__属性没有写入 ES6 的正文,而是写入了附录。

  2. 原因是它本质上是一个内部属性,而不是一个正式的对外的 API,只是由于浏览器广泛支持,才被加入了 ES6

  3. 标准明确规定,只有浏览器必须部署这个属性,其他运行环境不一定需要部署,而且新的代码最好认为这个属性是不存在的

  4. 所以无论从语义的角度,还是从兼容性的角度,都不要使用这个属性,应该使用:Object.getPrototypeOf(target)(读操作)、Object.setPrototypeOf(target)(写操作)、Object.create(target)(生成操作)代替

构造函数是什么、它做了什么

出自《你不知道的js》:在js中, 实际上并不存在所谓的’构造函数’,只有对于函数的’构造调用’。

上文一直提到构造函数,所谓的构造函数,实际上就是通过关键字new来调用的函数:

let newObj = new someFn() // 构造调用函数

构造/new调用函数的时候做了什么

  1. 创建一个全新的对象。
  2. 这个新对象的原型(Object.getPrototypeOf(target))指向构造函数的prototype对象。
  3. 该函数的this会绑定在新创建的对象上。
  4. 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。
  5. 我们称这个新对象为构造函数的实例。

原型继承就是利用构造调用函数的特性

SubType.prototype = new SuperType();  // 原型继承:SubType继承SuperType
SubType.prototype.constructor = SubType // 重新指定constructor指向 方便找到构造函数
// 挂载SuperType的this和prototype的属性和方法到SubType.prototype上
  1. 构造调用的第二点:将新对象的Object.getPrototypeOf(target)指向函数的prototype
  2. 构造调用的第三点:该函数的this会绑定在新创建的对象上。(所以父类this声明的属性被所有子类实例共享)
  3. 新对象赋值给SubType.prototype

原型链是什么

来看个例子:

function foo() { }
const newObj = new foo() // 构造调用foo 返回一个新对象
const newObj__proto__ = Object.getPrototypeOf(newObj) // 获取newObj的原型对象
newObj__proto__ === foo.prototype // true 验证newObj的原型指向foo
const foo__proto__ = Object.getPrototypeOf(foo.prototype) // 获取foo.prototype的原型
foo__proto__ === Object.prototype // true foo.prototype的原型是Object.prototype

如果用以前的语法,从newObj查找foo的原型,是这样的:

newObj.__proto__.__proto__ // 这种关系就是原型链

可以用以下三句话来理解原型链

  1. 每个对象都拥有一个原型对象: newObj的原型是foo.prototype
  2. 对象的原型可能也是继承其他原型对象的: foo.prototype也有它的原型Object.prototype
  3. 一层一层的,以此类推,这种关系就是原型链

一个对象是否在另一个对象的原型链上

如果一个对象存在另一个对象的原型链上,我们可以说:它们是继承关系。

判断方式有两种,但都是根据构造函数的prototype是否在原型链上来判断的:

  1. instanceof: 用于测试构造函数的prototype属性是否出现在对象的原型链中的任何位置

语法:object instanceof constructor

let test = function () { }
let testObject = new test();
testObject instanceof test // true test.prototype在testObject的原型链上testObject instanceof Function // false Function.prototype 不在testObject的原型链上
testObject instanceof Object // true Object.prototype在testObject的原型链上
  1. isPrototypeOf:测试一个对象是否存在于另一个对象的原型链上

语法:prototypeObj.isPrototypeOf(object)

let test = function () { }
let testObject = new test();
test.prototype.isPrototypeOf(testObject) // true test.prototype在testObject的原型链上
Object.prototype.isPrototypeOf(testObject) // true Object.prototype在testObject的原型链上

原型链的终点: Object.prototype

Object.prototype是原型链的终点,所有对象都是从它继承了方法和属性。

Object.prototype没有原型对象

const proto = Object.getPrototypeOf(Object.prototype) // null

下面是两个验证例子,有疑虑的同学多写几个测试用例印证一下。

字符串原型链的终点Object.prototype

参考 前端进阶面试题详细解答

let test = '由String函数构造出来的'
let stringPrototype = Object.getPrototypeOf(test) // 字符串的原型
stringPrototype === String.prototype // true 字符串的原型是String对象
Object.getPrototypeOf(stringPrototype) === Object.prototype // true String对象的原型是Object对象

函数原型链的终点:Object.prototype

let test = function () { }
let fnPrototype = Object.getPrototypeOf(test)
fnPrototype === Function.prototype // true test的原型是Function.prototype
Object.getPrototypeOf(Function.prototype) === Object.prototype // true

原型链用来做什么?

属性查找:

如果试图访问对象(实例instance)的某个属性,会首先在对象内部寻找该属性,直至找不到,然后才在该对象的原型(instance.prototype)里去找这个属性,以此类推

我们用一个例子来形象说明一下:

let test = '由String函数构造出来的'
let stringPrototype = Object.getPrototypeOf(test) // 字符串的原型
stringPrototype === String.prototype // true 字符串的原型是String对象
Object.getPrototypeOf(stringPrototype) === Object.prototype // true String对象的原型是Object对象

当你访问test的某个属性时,浏览器会进行以下查找:

  1. 浏览器首先查找test 本身
  2. 接着查找它的原型对象:String.prototype
  3. 最后查找String.prototype的原型对象:Object.prototype
  4. 一旦在原型链上找到该属性,就会立即返回该属性,停止查找。
  5. 原型链上的原型都没有找到的话,返回undefiend

这种查找机制还解释了字符串为何会有自带的方法: slice/split/indexOf等。

准确的说:

  • 这些属性和方法是定义在String这个全局对象/函数上的。
  • 字符串的原型指向了String函数的prototype
  • 之后通过查找原型链,在String函数的prototype中找到这些属性和方法。

拒绝查找原型链:

hasOwnProperty: 指示对象自身属性中是否具有指定的属性

语法:obj.hasOwnProperty(prop)

参数: prop 要查找的属性

返回值: 用来判断某个对象是否含有指定的属性的Boolean

let test ={ 'OBKoro1': '扣肉' }
test.hasOwnProperty('OBKoro1');  // true
test.hasOwnProperty('toString'); // false test本身没查找到toString 

这个API是挂载在object.prototype上,所有对象都可以使用,API会忽略掉那些从原型链上继承到的属性。

扩展:

实例的属性

你知道构造函数的实例对象上有哪些属性吗?这些属性分别挂载在哪个地方?原因是什么?

function foo() {this.some = '222'let ccc = 'ccc'foo.obkoro1 = 'obkoro1'foo.prototype.a = 'aaa'
}
foo.koro = '扣肉'
foo.prototype.test = 'test'
let foo1 = new foo() // `foo1`上有哪些属性,这些属性分别挂载在哪个地方
foo.prototype.test = 'test2' // 重新赋值

上面这道是考察JS基础的题,很多人都没说对,原因是没有彻底掌握this原型链函数

想一下再看解析:

想一下再看解析:

想一下再看解析:

想一下再看解析:

想一下再看解析:

  1. this.somefoo1对象的属性

通过构造调用foothis指向foo1,所以this.some挂载在foo1对象下。

属性查找: foo1.some

foo1.some直接读取foo1的属性。

  1. foo1.testfoo1.afoo1对象的原型

根据上文提到的:构造/new调用函数的时候会创建一个新对象(foo1),自动将foo1的原型(Object.getPrototypeOf(foo1))指向构造函数的prototype对象。

构造调用会执行函数,所以foo.prototype.a = 'aaaaa'也会执行,单就赋值这个层面来说写在foo外面和写在foo里面是一样的。

属性查找:foo1.testfoo1.a

  • foo1本身没有找到,继续查找

  • foo1的原型Object.getPrototypeOf(foo1)上找到了atest,返回它们,停止查找。

  1. foo1.obkoro1foo1.koro:返回undefined

静态属性: foo.obkoro1foo.koro

函数在JS中是一等公民,它也是一个对象, 用来模拟类。

这两个属性跟foo1没有关系,它是对象foo上的两个属性(类似函数的:arguments/prototype/length等属性),称为静态属性

它们只能通过foo.obkoro1foo.koro来访问。

原型对象改变,原型链下游获取的值也会改变

上面那个例子中的foo1.test的值是什么?

foo.prototype.test = 'test'
let foo1 = new foo() // `foo1`上有哪些属性,这些属性分别挂载在哪个地方
foo.prototype.test = 'test2' // 重新赋值

foo1.test的值是test2,原因是:foo1的原型对象是Object.getPrototypeOf(foo1)存的指针,指向foo.prototype的内存地址,不是拷贝,每次读取的值都是当前foo.prototype的最新值。

打印foo1

在这里插入图片描述

小结

写了好几天,之前网上很多图文博客,那些线指来指去,就我个人看来还是比较难以理解的,所以本文纯文字的形式来描述这些概念,相信认真看完的同学肯定都有所收获,如果没看懂的话,建议多看几遍,这部分概念真的很重要!

相关文章:

js对象和原型、原型链的关系

JS的原型、原型链一直是比较难理解的内容,不少初学者甚至有一定经验的老鸟都不一定能完全说清楚,更多的"很可能"是一知半解,而这部分内容又是JS的核心内容,想要技术进阶的话肯定不能对这个概念一知半解,碰到…...

【SpringBoot高级篇】SpringBoot集成Sharding-JDBC分库分表

【SpringBoot高级篇】SpringBoot集成Sharding-JDBC分库分表Apache ShardingSphere分库分表分库分表的方式垂直切分垂直分表垂直分库水平切分水平分库水平分表分库分表带来的问题分库分表中间件Sharding-JDBCsharding-jdbc实现水平分表sharding-jdbc实现水平分库sharding-jdbc实…...

Shell特殊字符

shell语言,一些字符是有特殊意义的。 根据作用分为几种特殊符号 一、空白 shell调用函数,不像c语言那样用把参数放到括号里,用逗号分隔。而是用空格作为参数之间,参数与函数名之间的分隔符。 换行符也是特殊字符。换行符用作一条命…...

【计算机二级python】综合题目

计算机二级python真题 文章目录计算机二级python真题一、德国工业战略规划二、德国工业战略规划 第一问三、德国工业战略规划 第二问一、德国工业战略规划 描述:在右侧答题模板中修改代码,删除代码中的横线,填写代码,完成考试答案。‪‬‪‬…...

字节直播leader面

设计评论系统(缓存怎么做) mysql是否有主从延迟,如何解决 mysql有主从延迟 主从延迟主要因为mysql主从同步的机制,mysql有三种同步机制 同步复制:事务线程等待所有从库复制成功响应异步复制:事务不等待…...

PIC 单片机的时钟

注意:本文的内容无法保证绝对精确,后续可能会做改动,只是自己的笔记。这里的资料均源自数据手册本身。PIC18系列单片机的参考时钟可以选择三个基础时钟源:Primary Clock, OSC1 or OSC2,Secondary Clock,Inner clock.时钟源分为两个…...

【数据结构】关于二叉树你所应该知道的数学秘密

目录 1.什么是二叉树(可以跳过 目录跳转) 2.特殊的二叉树(满二叉树/完全二叉树) 2.1 基础知识 2.2 满二叉树 2.3 完全二叉树 3.二叉树的数学奥秘(主体) 3.1 高度与节点个数 3.2* 度 4.运用二叉树的…...

哈希表题目:猜数字游戏

文章目录题目标题和出处难度题目描述要求示例数据范围解法一思路和算法代码复杂度分析解法二思路和算法代码复杂度分析题目 标题和出处 标题:猜数字游戏 出处:299. 猜数字游戏 难度 4 级 题目描述 要求 你在和朋友一起玩猜数字(Bulls…...

项目请求地址自动加上了本地ip的解决方式

一般情况下来说都是一些粗心大意的问题导致的 场景一:少加了/ 场景二:前后多加了空格 场景三:拼接地址错误![...

Vue3 企业级项目实战:项目须知与课程约定

本节内容很重要,希望大家能够耐心看完。 Vue3 企业级项目实战 - 程序员十三 - 掘金小册Vue3 Element Plus Spring Boot 企业级项目开发,升职加薪,快人一步。。「Vue3 企业级项目实战」由程序员十三撰写,2744人购买https://s.ju…...

传导EMI抑制-Π型滤波器设计

1 传导电磁干扰简介 在开关电源中,开关管周期性的通断会产生周期性的电流突变(di/dt)和电压突变(dv/dt),周期性的电流变化和电压变化则会导致电磁干扰的产生。 图1所示为Buck电路的电流变化,在Buck电路中上管电流和下…...

如何在excel中创建斐波那契数列

斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:…...

遮挡检测--基于角度的遮挡检测方法

文章目录1基于角度的遮挡检测方法2遮挡检测遍历方法2.1方法1--自适应径向扫描方法2.2方法2--螺旋扫描法参考1基于角度的遮挡检测方法 在基于角度的方法中,通过依次分析DSM中沿径向方向的投影光线的角度来识别遮挡。定义α\alphaα角:DSM三维点与相机中心…...

【luogu CF1098D】Eels(结论)

Eels 题目链接:luogu CF1098D 题目大意 有一个可重集,每次操作会放进去一个数或者取出一个数。 然后每次操作完之后,问你对这个集合进行操作,每次选出两个数 a,b 加起来合并回去,直到集合中只剩一个数,要…...

【java】遍历文件夹输出所有文件的文件名与绝对路径,在windows环境

【java】遍历文件夹输出所有文件的文件名与绝对路径,在windows环境 String filepath "D:\\CloudMusic\\";//D盘下的file文件夹的目录File file new File(filepath);//File类型可以是文件也可以是文件夹File[] fileList file.listFiles();//将该目录下的…...

Window问题详解(下)

建议先看一下 Window问题详解(上) 思路② 既然会超时,那该怎么办呢? 显然需要一个更快速的方法来解决这个问题! 我们先来观察一下图片: 我们发现,每一次选中的数都会增加下一个。 !!!!! 因此,我们可以根据此特性优化时间!! 第一次先求出前 k − 1 k-1 k−...

Kafka部署与SpringBoot集成

Kafka与ZooKeeper Apache ZooKeeper是一个基于观察者模式的分布式服务管理框架,即服务注册中心。同时ZooKeeper还具有存储数据的能力。Kafka的每台服务器作为一个broker注册到ZooKeeper,多个broker借助ZooKeeper形成了Kafka集群。同时ZooKeeper会保存一…...

c++11 标准模板(STL)(std::unordered_set)(十三)

定义于头文件 <unordered_set> template< class Key, class Hash std::hash<Key>, class KeyEqual std::equal_to<Key>, class Allocator std::allocator<Key> > class unordered_set;(1)(C11 起)namespace pmr { templ…...

【2023】DevOps、SRE、运维开发面试宝典之ELKStack相关面试题

文章目录 1、elasticsearch的应用场景2、elasticsearch的特点3、Elasticsearch集群三种状态分别是什么?代表什么?4、Elasticsearch集群的优化方面5、Elasticsearch集群防止脑裂的配置参数?6、ELK日志采集平台架构组件介绍?7、Logstash组件的作用?8、收集Kubernetes集群程序…...

Hive中的高阶函数(二)

1、UDTF之explode函数 explode(array)将array列表里的每个元素生成一行&#xff1b; explode(map)将map里的每一对元素作为一行&#xff0c;其中key为一列&#xff0c;value为一列&#xff1b; 一般情况下&#xff0c;explode函数可以直接使用即可&#xff0c;也可以根据需要结…...

基于当前项目通过npm包形式暴露公共组件

1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹&#xff0c;并新增内容 3.创建package文件夹...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命

在华东塑料包装行业面临限塑令深度调整的背景下&#xff0c;江苏艾立泰以一场跨国资源接力的创新实践&#xff0c;重新定义了绿色供应链的边界。 跨国回收网络&#xff1a;废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点&#xff0c;将海外废弃包装箱通过标准…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

无人机侦测与反制技术的进展与应用

国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机&#xff08;无人驾驶飞行器&#xff0c;UAV&#xff09;技术的快速发展&#xff0c;其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统&#xff0c;无人机的“黑飞”&…...

uniapp 字符包含的相关方法

在uniapp中&#xff0c;如果你想检查一个字符串是否包含另一个子字符串&#xff0c;你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的&#xff0c;但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化

iOS 应用的发布流程一直是开发链路中最“苹果味”的环节&#xff1a;强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说&#xff0c;这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发&#xff08;例如 Flutter、React Na…...

Vue 3 + WebSocket 实战:公司通知实时推送功能详解

&#x1f4e2; Vue 3 WebSocket 实战&#xff1a;公司通知实时推送功能详解 &#x1f4cc; 收藏 点赞 关注&#xff0c;项目中要用到推送功能时就不怕找不到了&#xff01; 实时通知是企业系统中常见的功能&#xff0c;比如&#xff1a;管理员发布通知后&#xff0c;所有用户…...

从实验室到产业:IndexTTS 在六大核心场景的落地实践

一、内容创作&#xff1a;重构数字内容生产范式 在短视频创作领域&#xff0c;IndexTTS 的语音克隆技术彻底改变了配音流程。B 站 UP 主通过 5 秒参考音频即可克隆出郭老师音色&#xff0c;生成的 “各位吴彦祖们大家好” 语音相似度达 97%&#xff0c;单条视频播放量突破百万…...