详解链表在前端的应用,顺便再弄懂原型和原型链!

链表在前端中的应用

  • 一、链表VS数组
  • 二、JS中的链表
  • 三、前端与链表:JS中的原型链
    • 1、原型是什么?
    • 2、原型链是什么?
    • 3、原型链长啥样?
      • (1)arr → Array.prototype → Object.prototype → null
      • (2)obj → Object.prototype → null
      • (3)func → Function.prototype → Object.prototype → null
      • (4)class中的原型
      • (5)new Object() 和 Object.create() 区别
    • 4、原型链知识点
    • 5、常见面试题
      • (1)instanceof原理
      • (2)看代码,得出输出结果。
  • 四、写在最后

链表 在前端中的应用常用于原型和原型链当中。在接下来的这篇文章中,将讲解关于 链表 在前端中的应用。

一、链表VS数组

  • 数组:增删非首尾元素时往往需要移动元素
  • 链表:增删非首尾元素,不需要移动元素,只需要更改 next 的指向即可。

二、JS中的链表

  • Javascript中没有链表;
  • 可以用Object模拟链表。

三、前端与链表:JS中的原型链

1、原型是什么?

  • Javascript 中,每个对象都会在其内部初始化一个属性,这个属性就是原型对象(简称原型)。

2、原型链是什么?

  • 当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去 prototype 里找这个属性,这个 prototype 又会有自己的 prototype ,于是就这样一直找下去,这样逐级查找形似一个链条,且通过 [[prototype]] 属性连接,这个连接的过程被称为原型链
  • 原型链的本质是链表,且原型链上的节点是各种原型对象,如: Function.prototypeObject.prototype ……。
  • 原型链通过 __proto__ 属性连接各种原型对象。

3、原型链长啥样?

(1)arr → Array.prototype → Object.prototype → null

  • arr.__ proto __ = Array.prototype;
  • Array.__ proto __= Object.prototype;
  • Object.__ proto __= null。

先用代码来演示这段关系:

let arr = [];
console.log(arr.__proto__ === Array.prototype);    //  true
console.log(arr.__proto__.__proto__ === Object.prototype);    //  true
console.log(arr.__proto__.__proto__.__proto__);  //null

解释说明:

假设我们定义了一个对象,名字叫 arr ,那么 arr.__proto__ 表示的是arr这个对象的原型,在这个例子中 let arr = [] 间接调用了 new Array ,所以我们通过 Array.prototype 来表示 Array 这个构造函数的原型对象,通过对 arr.__proto__Array.prototype 进行比较,发现两者相等,所以说, arr 的原型属性就是构造函数 Array 的原型对象。

与上述类似的,我们发现 arr.__proto__Array.prototype 相等,那么继续往源头查找下去, Array 又有它自己的原型属性,那么这个时候 Array 的原型属性 arr.__proto__.__proto__ 又会等于什么呢?

其实,在 js 当中, Object 是所有对象的父对象,也就是说绝大多数的对象都有一个共同的原型 Object.prototype 。所以,这个时候 Array 的原型属性 arr.__proto__.__proto__ 就等于 Object.prototype ,到此为止,找到最原始的对象 Object 的原型之后,基本就快结束了。我们最后再检验 Object 的原型属性 arr.__proto__.__proto__.__proto__ ,发现是 null 空值,也就意味着原型链已经走到了最源头的位置。

总结:

  • Object 是所有对象的父对象
  • 从上面例子中可以看到,所有原型对象都会先指向自己的 __proto__ 属性,之后再指向自己的原型,最后指向父对象 Object 的原型。

下面再给出两个例子,大家可以依据(1)的方法进行检验。

(2)obj → Object.prototype → null

  • obj.__ proto __ = Object.prototype;
  • Object.__ proto __= null。

用代码来演示这段关系:

let obj = {};
console.log(obj.__proto__ === Object.prototype);    //  true
console.log(obj.__proto__.__proto__);  //null

(3)func → Function.prototype → Object.prototype → null

  • func.__ proto __ = Function.prototype;
  • Function.__ proto __= Object.prototype;
  • Object.__ proto __= null。

用代码来演示这段关系:

let func = function(){};
console.log(func.__proto__ === Function.prototype);    //  true
console.log(func.__proto__.__proto__ === Object.prototype);    //  true
console.log(func.__proto__.__proto__.__proto__);  //null

(4)class中的原型

1)先来看第一段代码。

//父类
class People{constructor(){this.name = name;}eat(){console.log(`${this.name} eat something`);}
}//子类
class Student extends People{constructor(name, number){super(name);this.number = number;}sayHi(){console.log(`姓名:${this.name},学号:${this.number}`);}
}console.log(typeof Student); //function
console.log(typeof People); //functionlet xialuo = new Student('夏洛', 10010);
console.log(xialuo.__proto__);
console.log(Student.prototype);
console.log(xialuo.__proto__ === Student.prototype); //true

从上面代码中可以看到, typeof Studenttypeof People 的值是 function ,所以 class 实际上是函数,也就是语法糖。

再看下面三个 console.log 打印的值,我们来梳理一个原型间的关系。首先 Student 是一个 class ,那么每个 class 都有它的显式原型 prototype ,而 xialuo 是一个实例,每个实例都有它的隐式原型 __proto__ 。它们两者之间的关系就是,实例 xialuo__proto__ 指向对应的 class (即 Student )的 prototype

因此,对于class中的原型,可以得出以下结论:

  • 每个 class 都有显式原型 prototype
  • 每个实例都有隐式原型 __proto__ ;
  • 实例的 __proto__ 指向对应 classprototype

2)再来看第二段代码。

//父类
class People{constructor(){this.name = name;}eat(){console.log(`${this.name} eat something`);}
}//子类
class Student extends People{constructor(name, number){super(name);this.number = number;}sayHi(){console.log(`姓名:${this.name},学号:${this.number}`);}
}let xialuo = new Student('夏洛', 10010);
console.log(xialuo.name); //夏洛
console.log(xialuo.number); //10010
console.log(Student.sayHi()); //姓名:夏洛,学号:10010

从上面代码中可以得出, Student 类本身有 number 这个属性,所以它会直接读取自身 number 的值。同时,它是没有 name 这个属性的,但是由于它继承自父类 People ,所以当它找不到 name 则个属性时,它会自动的往 __proto__ 中查找,于是就往它的父类 People 进行查找。

所以,从上面的演示中可以得出基于原型的执行规则:

  • 先获取属性(比如 xialuo.namexiaoluo.number ) 或者获取执行方法 (比如 xialuo.sayhi() );
  • 获取后,先在自身属性和方法上寻找;
  • 如果找不到则自动去 __proto__ 中查找。

(5)new Object() 和 Object.create() 区别

  • {} 等同于 new Object() ,原型为 Object.prototype
  • Object.create(null) 没有原型;
  • Object.create({...}) 可指定原型。

4、原型链知识点

1)如果 A 沿着原型链能找到 B.prototype ,那么 A instanceof Btrue

举例1:

let obj = {};
console.log(obj instanceof Object); //true

对于 obj instanceof Object 进行左右运算, obj instanceof Object 的意思是查询 obj 的原型链上是否有 Object 的原型对象,即 obj 是否是 Object 的实例。

举例2:

let func = function(){};
console.log(func instanceof Function); //true
console.log(func instanceof Object); //true

对于 func 来说, func 既是 Function 的实例,也是 Object 的实例。

2)如果 A 对象上没有找到 x 属性,那么会沿着原型链找 x 属性。

举例:

const obj = {};Object.prototype.x = 'x';console.log(obj.x); //x

从上述代码中可以看到,obj 在自己的区域内没有找到x的值,则会继续往它的原型链找,最终找到 Object.prototype.x ,所以 obj.x = x

接下来我们用两道常见的面试题来回顾这两个知识点。

5、常见面试题

(1)instanceof原理

知识点:如果 A 沿着原型链能找到 B.prototype ,那么 A instanceof Btrue

解法:遍历 A 的原型链,如果找到 B.prototype ,返回 true ,否则返回 false

代码演示:

// 判断A是否为B的实例
const instanceOf = (A, B) => {// 定义一个指针P指向Alet p = A;// 当P存在时则继续执行while(p){// 判断P值是否等于B的prototype对象,是则说明A是B的实例if(p === B.prototype){return true;}// 不断遍历A的原型链,直到找到B的原型为止p = p.__proto__;}return false;
}

(2)看代码,得出输出结果。

看下面一段代码,请给出4个 console.log 打印的值。

let foo = {};
let F = function(){};
Object.prototype.a = 'value a';
Function.prototype.b = 'value b';console.log(foo.a); //value a
console.log(foo.b); //undefindconsole.log(F.a); //value a
console.log(F.b); //value b

知识点:如果在 A 对象上没有找到 x 属性,那么会沿着原型链找 x 属性。

解法:明确 fooF 变量的原型链,沿着原型链找 A 属性和 B 属性。

解析:从上面一段代码中可以看到, foo 是一个对象,那么它的 __proto__ 属性指向 Object.prototype ,所以此时 foo.a 会往它的原型链上面找具体的值,也就是 Object.prototype.a 的值。同理, foo.b 会往它的原型链找值,但是找不到 Object.prototype.b 的值,所以最终返回 undefinedF.aF.b 也是同样的道理,大家可以进行一一验证。

四、写在最后

原型和原型链在前端中是再基础不过的知识了!我们平常所写的每一个对象中,基本上都有它的原型和原型链。因此,对于前端来说,如果原型和原型链的关系都不明白的话,不知不觉中很容易写出各种各样的bug,这对于后续维护和程序来说都是一个巨大的灾难。所以,了解原型和原型链,对于前端来说是一项必备的技能。

链表在前端中的应用就讲到这里啦!如果有不理解或者有误的地方也欢迎私聊我或加我微信指正~

  • 公众号:星期一研究室
  • 微信:MondayLaboratory

创作不易,如果这篇文章对你有用,记得点个 Star 哦~

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

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

相关文章

计网课设 模拟实验拓扑

一:前言 幸福的是考完两科了,抽出时间赶紧赶赶课设,两周遇到了好多事,开心的,烦心的,一堆堆的压过来,但庆幸自己迷失不太久,又能继续学习了 二:实验概览 1:实验拓扑图 2:实验要求…

快醒醒,C# 9 中又来了一堆关键词 init,record,with

一:背景1. 讲故事.NET5 终于在 2020-08-25 也就是前天发布了第八个预览版,这么多的预览版搞得我都麻木了,接踵而来的就是更多的新特性加入到了 C# 9 中,既然还想呆在这条船上,得继续硬着头皮学习哈,这一篇跟…

7-1 字母统计图 (10 分)(思路+详解)

一&#xff1a;题目 摆放在面前的是一小段英文文章。 afeng希望你能帮他统计一下每个小写字母出现的次数。 最后再以柱状图的形式(参照输出样例)输出出来。 输入格式: 输入第一行为一个正整数N(N<100)&#xff0c;表示文章的行数。 随后为N行文本。 输出格式: 由若干行组…

了解js基础知识中的作用域和闭包以及闭包的一些应用场景,浅析函数柯里化

js基础知识中的作用域和闭包一、作用域1、作用域、自由变量简介&#xff08;1&#xff09;作用域定义&#xff08;2&#xff09;作用域实例演示&#xff08;3&#xff09;自由变量定义&#xff08;4&#xff09;自由变量实例演示2、作用域链简介&#xff08;1&#xff09;作用域…

张朝阳一天只睡4小时?不知道,反正我每天都睡足7小时

这是头哥侃码的第213篇原创周末&#xff0c;一个很久没联系过的朋友突然在微信上发给我一个链接。我打开一看&#xff0c;原来是搜狐老板张朝阳近日发表的一个有关睡眠的神论&#xff0c;大致是说他每天只睡四小时&#xff0c;白天状态还特别好&#xff0c;每天员工到公司的时候…

Istio 1.7——进击的追风少年

2020 年 8 月 21 日&#xff0c;Istio 发布了 1.7 版本。除了介绍新版本的主要更新内容外&#xff0c;本文会重点分析 Istio 团队在产品更新策略上的激进态度和举措。是稳扎稳打做好向后兼容&#xff0c;带给用户所承诺的易用性&#xff1b;还是快刀斩乱麻&#xff0c;做进击的…

7-2 港口审查 (15 分)

一:题目 afeng是一个港口的海关工作人员&#xff0c;每天都有许多船只到达港口&#xff0c;船上通常有很多来自不同国家的乘客。 afeng对这些到达港口的船只非常感兴趣&#xff0c;他按照时间记录下了到达港口的每一艘船只情况&#xff1b;对于第i艘到达的船&#xff0c;他记…

JS中this的应用场景,再了解下apply、call和bind!

this的应用场景&#xff0c;再了解下apply、call和bind一、谈谈对this对象的理解二、this的应用场景1、作为普通函数被调用2、使用call、apply和bind被调用3、作为对象方法被调用4、在class方法中被调用5、箭头函数中被调用三、apply、call和bind1、apply、call和bind的共同用法…

【BCVP更新】StackExchange.Redis 的异步开发方式

有哪些习惯坚持LESS IS MORE,SIMPLER IS BETTER THAN MORE你一定会有很大的收获各种小问题&#xff1f;如果你之前用过Redis的话&#xff0c;肯定会使用过StackExchange.Redis&#xff0c;我之前很久就用过&#xff0c;在.netfw的时候&#xff0c;当时并发还比较小&#xff0c;…

map容器实现一对多

一&#xff1a;需求描述 我们希望一个数字或则其他字符串可以对应 一串数&#xff0c; #include<iostream> #include<map> #include<vector> using namespace std; int main(){map<int,vector<int> > m;map<int,vector<int> >:: i…

解决异步问题,教你如何写出优雅的promise和async/await,告别callback回调地狱!

解决异步问题——promise、async/await一、单线程和异步1、单线程是什么2、为什么需要异步3、使用异步的场景二、promise1、promise的三种状态2、三种状态的表现和变化&#xff08;1&#xff09;状态的变化&#xff08;2&#xff09;状态的表现3、then和catch对状态的影响&…

使用 Visual Studio 2019 批量添加代码文件头

应用场景介绍在我们使用一些开源项目时&#xff0c;基本上都会在每个源代码文件的头部看到一段版权声明。一个项目或解决方案中源代码文件的个数少则几十&#xff0c;多则几千甚至更多&#xff0c;那么怎么才能给这么多文件方便地批量添加或者修改一致的文件头呢&#xff1f;在…

7-3 模板题 (10 分)(思路+详解)

一:题目 二&#xff1a;思路 1.读题读不懂&#xff0c;那就分析给出的示例&#xff0c;本题意思就是给出一串数&#xff0c;然后找出找出该元素之后&#xff0c;第一个大于 该元素的下标&#xff08;这一串数的下标是从一开始的&#xff09;如果找不到比起大的&#xff0c;那就…

提升对前端的认知,不得不了解Web API的DOM和BOM

了解Web API的DOM和BOM引言正文一、DOM操作1、DOM的本质2、DOM节点操作&#xff08;1&#xff09;property形式&#xff08;2&#xff09;attribute形式3、DOM结构操作&#xff08;1&#xff09;新增/插入节点&#xff08;2&#xff09;获取子元素列表&#xff0c;获取父元素&a…

Dapr微服务应用开发系列1:环境配置

题记&#xff1a;上篇Dapr系列文章简要介绍了Dapr&#xff0c;这篇来谈一下开发和运行环境配置本机开发环境配置安装Docker为了方便进行Dapr开发&#xff0c;最好&#xff08;其实不一定必须&#xff09;首先在本机&#xff08;开发机器&#xff09;上安装Docker。安装方式可以…

leetcode704二分法:(左闭右闭+左闭右开)

前言 又重温了一遍<肖生客的救赎> 其中安迪的一句话一直回荡我的脑中&#xff1a;“人生可以归结为一种简单的选择&#xff1a;不是忙着活&#xff0c;就是忙着死。” 多深刻&#xff0c;多简单&#xff0c;又多令人深省&#xff0c; 哪有那么多选择 哪有那么多时间去花…

你真的理解事件绑定、事件冒泡和事件委托吗?

一文了解Web API中的事件绑定、事件冒泡、事件委托引言正文一、事件绑定1、事件和事件绑定时什么&#xff1f;2、事件是如何实现的&#xff1f;二、事件冒泡1、事件模型2、事件模型解析&#xff08;1&#xff09;捕获阶段&#xff08;2&#xff09;目标阶段&#xff08;3&#…

欢迎来到 C# 9.0(Welcome to C# 9.0)

翻译自 Mads Torgersen 2020年5月20日的博文《Welcome to C# 9.0》&#xff0c;Mads Torgersen 是微软 C# 语言的首席设计师&#xff0c;也是微软 .NET 团队的项目群经理。C# 9.0 正在成形&#xff0c;我想和大家分享一下我们对下一版本语言中添加的一些主要特性的想法。对于 C…

367. 有效的完全平方数(二分法)

一&#xff1a;题目 二:思路 完全平方数:若一个数能表示成某个整数的平方的形式&#xff0c;则称这个数为完全平方数 思路:1.我们将num先折半,因为它是某个整数的平方&#xff0c;而这个数的范围肯定不会超过num的一半 2.那么这就相当于在[left,num/2]中查找某个数&#xff0c…

跨越跨域大山,前端不得不知道的Ajax

AJAX和跨域引言正文一、AJAX请求1、模拟get和post请求2、封装一个简易的AJAX二、状态码1、xhr.readyState2、xhr.status三、跨域1、同源策略&#xff08;1&#xff09;同源策略是什么&#xff08;2&#xff09;为什么浏览器会有同源策略&#xff1f;&#xff08;3&#xff09;同…