掌握原型链,再炒冷饭系列

简介: 我们知道每一个函数都有一个自身的prototype,每一个对象都有__proto__对象,而这个__proto__我们常称之为隐式原型,正因为它连接起了对象与构造函数的关系。当我们访问一个对象时,首先会在自身属性上找,当自身属性找不到时,会到对象的隐式链上去找,如果隐式链上还没有,那么会到构造函数的原型上找,当原型上没有时,会到原型的隐式__proto__上去找,当这个属性还找不到时,就直接返回undefined了,因此才形成了一条原型链。

原型链是一个比较抽象的概念,每当被问起这个问题时,总会回答得不是那么准确,好像懂,但自己好像又不太懂,真是尴尬了


正文开始...


我们知道每一个函数都有一个自身的prototype,每一个对象都有__proto__对象,而这个__proto__我们常称之为隐式原型,正因为它连接起了对象与构造函数的关系。


当我们访问一个对象时,首先会在自身属性上找,当自身属性找不到时,会到对象的隐式链上去找,如果隐式链上还没有,那么会到构造函数的原型上找,当原型上没有时,会到原型的隐式__proto__上去找,当这个属性还找不到时,就直接返回undefined了,因此才形成了一条原型链。


针对以上一段长长的话,我们用实际例子来佐证一下

function Person() {
  this.name = 'Maic';
  this.age = 18;
}
Person.prototype.say = function () {
  return `hello ${this.name}`
}
const person = new Person();

我们访问

console.log(person.name); // Maic

现在我在原型上找

function Person() {
  // 1 this.name = 'Maic';
  this.age = 18;
}
// 3 Person.prototype.name = 'Test';
// 4 Person.prototype.__proto__.name = '999'
Person.prototype.say = function () {
  return `hello ${this.name}`
}
const person = new Person();
// 2 person.__proto__.name = '8888'
console.log(person.say());

从结果上来看,会是依次从1,2,3,4依次查找下去,直到最后找不到name为止,最后就打印undefined


当我们对构造函数实例化的时候,此时就会返回一个对象person,然而这个person对象就可以访问构造函数内部的属性,以及原型上的方法了。


这个person对象为什么可以访问构造函数的属性?以及构造函数原型上的方法?


那是因为通过__proto__这个隐式原型指向的构造函数的prototype


new过程


在上面例子中我们看到有用实例化构造函数,在面试中常有问题到,new的过程中发生了啥?


1、创建了一个对象


2、将这个对象的__proto__指向了构造函数的prototype


3、执行构造函数内部方法,并改变构造函数内部的this指向到新对象中


4、返回该构造函数的结果


我们根据以上几点,实现一个类似new的操作,从而真正理解new原生的实现


function mynew(Fn, ...arg) {
  // 1、创建一个对象
  const ret = {};
  // 2 将这个对象__proto__执行构造函数的prototype
  ret.__proto__ = Fn.prototype
  // or
  // Object.setPrototypeOf(ret, Fn.prototype);
  const result = Fn.call(ret, ...arg);
  return typeof result === 'object' ? result : ret;
}


对象与构造函数关系


我们再重新看下这段代码

function Person() {
  this.name = 'Maic';
  this.age = 18;
}
Person.prototype.say = function () {
  return `hello ${this.name}`
}
const person = new Person();

看下下面的几个判断

...
console.log(person.__proto__ === Person.prototype) // true
console.log(Person.__proto__ === Function.prototype) // true
console.log(Person.__proto__ === Object.__proto__); // true
console.log(Function.prototype === Object.__proto__); // true
console.log(Person.prototype.__proto__ === Object.prototype) // true
console.log(Object.prototype.__proto__ === null); // true
console.log(person.__proto__.__proto__ === Object.prototype) // true
console.log(Function.prototype.__proto__ === Object.prototype); // true

上面的关系画了一个图, 可能更直观点

73efe4b616e4b3a3f5fbab8a59581d9e.png

这个图看起来貌似还是不太容易记住,多理解几遍,应该会掌握八九不离十


另外还有一篇github上关于伢羽老师的原型链[1]的文章可以一同参考


总结


  • 理解原型链,每一个函数都有一个原型prototype,每一个对象都有自己的隐式__proto__,当我们访问对象属性时,会优先在自己内部属性寻找,然后会找__proto__上的属性,然后会去构造函数的prototype上寻找,如果构造函数的prototype找不到,会到到构造函数prototype__proto__上寻找,最后找不到该属性就返回undefined
  • 了解new操作背后的本质
  • 了解构造函数与实例对象的关系
  • 本文示例code example[2]
相关文章
|
移动开发 前端开发 测试技术
关于前端AB实验,我是这么思考的
背景 大家好, 我是Fly哥, 这次分享的内容主要是关于ABtest ,我们是做用户增长的,说白了就是对应下面几个关键词。拉新、激活、留存,留存的话 又分为 次日留存、 3日留存,这些都是我们的指标, 但是产品设计一个需求的时候, 可能会有实验的性质,不确定哪一组实验,对于指标的反馈是正向的,或者是那一组实验的效果更加明显。 这时候产品就会去创建AB实验,然后拿线上的一部分流量,去做实验, 分析数据, 得出实验结论,然后看是否满足预期, 如果不满足 就暂停实验, 或者进行全量实验。 大家可以看下下面这张流程图: 图片 然后对于我们前端而言,我们关心的点只有两个 第一个就是接入ABtest,
关于前端AB实验,我是这么思考的
|
存储 缓存 数据库
数据库数据删除策略:硬删除vs软删除的最佳实践指南
在项目开发中,“删除”操作常见但方式多样,主要分为硬删除与软删除。硬删除直接从数据库移除数据,操作简单、高效,但不可恢复;适用于临时或敏感数据。软删除通过标记字段保留数据,支持恢复和审计,但增加查询复杂度与数据量;适合需追踪历史或可恢复的场景。两者各有优劣,实际开发中常结合使用以满足不同需求。
1220 4
|
Java 关系型数据库 MySQL
mysql5.7 jdbc驱动
遵循上述步骤,即可在Java项目中高效地集成MySQL 5.7 JDBC驱动,实现数据库的访问与管理。
3046 1
|
开发框架 前端开发 JavaScript
前后端分离,Asp.net core webapi 如何配置跨域
前后端分离,Asp.net core webapi 如何配置跨域
615 0
|
机器学习/深度学习 算法 Python
【2023 华数杯全国大学生数学建模竞赛】 A题 隔热材料的结构优化控制研究 问题分析、模型建立及参考文献
本文提供了2023年华数杯全国大学生数学建模竞赛A题的详细分析、数学模型建立及参考文献,聚焦于隔热材料的结构优化控制研究,旨在解决单根隔热材料纤维的热导率测量难题,并探讨如何通过优化织物编织结构来提升隔热性能。
264 0
【2023 华数杯全国大学生数学建模竞赛】 A题 隔热材料的结构优化控制研究 问题分析、模型建立及参考文献
|
存储 JSON JavaScript
【嵌入式开源库】cJSON的使用,高效精简的json解析库
【嵌入式开源库】cJSON的使用,高效精简的json解析库
968 0
|
Java jenkins 应用服务中间件
09-Tomcat服务配置为Windows自启动服务
09-Tomcat服务配置为Windows自启动服务
|
JavaScript 前端开发
带你读《现代TypeScript高级教程》十五、装饰器与反射元数据(1)
带你读《现代TypeScript高级教程》十五、装饰器与反射元数据(1)
218 0
|
自然语言处理 Unix Linux
常用输入法框架简介
常用输入法框架简介
752 0
|
存储 容器
leetcode每日刷题
leetcode每日刷题
leetcode每日刷题