欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JS面向对象原理(二)------图解原型链(详)

程序员文章站 2022-03-26 21:47:58
JS中的数据类型分为两大类,一类是基本类型:String、Number、Boolean、Symbol、null、undefined,另一类是引用类型:Object、Array、Function。JS中有句话叫‘一切皆对象’,函数实际上也是对象。那基本类型也是对象吗?首先抛出我的结论,基本数据类型不是对象,在存储形式上它被存放在栈中,但是在一定条件下会自动转为对象()就是原始类型的‘包装对象’。......

引言

上周晚上有个朋友让我给她讲关于原型链的知识,我以为这都是小菜一碟,但是一番讲解下来才发现原来我熟悉的也不过是原型链的一小部分,当往上讲到Function和Object以及内置构造函数的时候,就有些牵强了。于是打算写一系列关于原型链这部分的内容。那就写吧,那要从哪里开始写起呢?说原型链,我想到的是为什么会有原型链,什么是原型链,利用原型链我们可以做什么。所以这系列的内容也会按照这个顺序来写。
为什么会有原型链:请移步到JS面向对象原理之面向对象编程发展史
本章则讲述什么是原型链。

JS对象

为了能够清楚的解释这一切,我先从对象讲起。
JS中的数据类型分为两大类,一类是基本类型:String、Number、Boolean、Symbol、null、undefined,另一类是引用类型:Object、Array、Function。JS中有句话叫‘一切皆对象’,你可以把对象理解为像map那样的数据结构,也可以理解为一张散列表,总之,在对象中数据是以key:value的形式存储。

对象三要素:唯一标志,状态,行为
著名的哲学问题 “忒修斯之船”是一种有关身份更替的悖论:如果忒修斯的船上的木头被逐渐替换,直到所有的木头都不是原来的木头,那这艘船还是原来的那艘船吗。所以对象的唯一标志就起作用了,在JS中对象的唯一标志是内存地址。对象要有状态,状态是可以被改变的,改变就是行为。这样对象的三要素就成立了。

函数实际上也是对象。那基本类型也是对象吗?
JS面向对象原理(二)------图解原型链(详)
从上图分析:
1.定义String数据有两种方式,使用JS内置的构造函数String或者使用字面量来定义。
2.如果使用构造函数来定义,则会生成一个String对象,typeof a===object。可以调用对象中存储的length方法,也可以调用其__proto__中存储的原型对象中的方法如toUpperCase()
3.如果使用字面量定义,则生成一个原始值(PrimitiveValue),typeof b===string。原始值中并没有定义任何的方法。但是依旧可以使用length方法和toUpperCase方法,为什么呢?

这是因为虽然基本数据类型不是对象,在存储形式上它被存放在栈中,但是在一定条件下(new关键字或者调用基本数据类型方法时)JavaScript 引擎会自动将原始类型的值转为包装对象实例(undefine和null除外),并在使用后立刻销毁实例。这就是原始类型的‘包装对象’,在上节中‘new语法糖为我们做了什么’部分有提到过关于包装对象的知识。

JS原型对象

我们现在知道了什么是对象了,那什么是原型对象呢?JS面向对象原理之面向对象编程发展史里我总结了一句话:原型对象是被设计来存放类的公有属性和方法的,自定义构造函数的原型除了自带一个不可枚举的constructor属性指向函数本身外,和其他空对象并无不同,而所有内置构造函数原型上还另外定义了实例方法,如Array.prototype.sort()。你可以把原型对象理解为一个共享仓库。当然你如果觉得这句话还是很抽象的话,我再给大家举个栗子吧。
在自然界中,万物皆对象。那么我们用‘对象’来表示万物。万物又分为动物和植物,这叫分类。动物又分为人类,鱼类…这就是更加详细的类的划分了。在人类中有2个人,一个叫CSS,一个叫CHH。她们是两个长得一模一样的人,俗称双胞胎。有一天CSS走在路上不小心摔了一个大跟斗,脚摔断了,可是CHH却安然无恙,因为虽然她们两个长得一模一样,但是却是两个独立的对象,互相不受影响。那这跟原型对象有什么关系呢?现在假设有两条鱼,一条叫CSS,一条叫CHH,这两条鱼也是双胞胎。那我怎么区分这个CSS是鱼,还是人呢?其实我们只要分别把鱼类和人类的公共特征提取出来,放到一个对象中去描述(事实上的确就是这么做的)。这样我们只要看这个CSS符合的是哪一类的特征就知道她是鱼还是人了。那么这个用于存放公共特征的对象就叫原型对象。
JS面向对象原理(二)------图解原型链(详)
也就是说在JS中有一些对象叫原型

typeof Object.prototype==='object'//true

Object的原型是Object.prototype,存放Object’类’的公共属性和方法。
Function的原型是Function.prototype,存放Function’类’的公共属性和方法
Person的原型是Person.prototype,存放Person’类’的公共属性和方法

JS构造函数之父Function与对象之父Object的纠葛

我们知道JavaScript是一种基于对象的语言。在JavaScript中,默认情况下,所有引用类型都继承自Object(通过原型链实现),任何函数的默认原型都是一个Object的实例。而所有构造函数的爸爸都是Function。包括在JS中九个原生(或内置)对象构造函数,都是Function的实例。
JS面向对象原理(二)------图解原型链(详)
看到红色箭头指向的两行了吗,Function是function类型好说,为什么Object也是function类型呢?其实这也不难理解,毕竟Object实际上也是个构造函数f Object(){[navtie code]}。实际上,Object被设定为函数是因为Object需要创建更多实例:

JS面向对象原理(二)------图解原型链(详)

那有人又会问了:JS面向对象原理(二)------图解原型链(详)
你看这个Function instanceof ObjectObject instanceof Function,字面意思看来是不是说这两个互为彼此的实例啊?
我先回答一下这个问题:Object的确是Function的实例,实际上它是个构造函数嘛。但是Function并不是Object的实例,只不过Function的原型是Object实例。Function作为一个构造函数之父,它自己实例化了自己。Object作为一个对象之父,它构造了所有的对象,包括原型。而instanceof的判断依据是:只要实例的原型链中包含了某构造函数的原型,则instanceof对该构造函数返回true

LOOK THIS PICTURE:
JS面向对象原理(二)------图解原型链(详)
a是我们创建的类,叫a类,实际上它是一个构造函数,所以typeof a===‘function’。b是a类的实例,实际上它是一个对象,再结合new的机制来看,可以以为,Function是赋予JS运行能力的工厂,Object则是为生产线提供了原料(容器)。
最后一句话概括。

Object和Function都是JavaScript的基类,JavaScript中所有的构造函数都需要Function来实例化,包括Object。JavaScript中所有的构造函数的原型都需要Object来实例化。

JS原型链

原型讲的差不多了,那原型链是什么,ECMA-262把原型链定义为ECMAScript的主要继承方式。其基本思想就是通过原型继承多个引用类型的属性和方法。再通俗一点来说,原型链就是把原型链起来的链子。

原型链三大规则
1.构造函数的prototype指向其原型对象
2.原型对象的constructor指向构造函数本身
3.实例对象的__proto__指向构造函数的原型对象
也就是说构造函数和实例之间并没有直接的联系。和实例之间有直接联系的是构造函数的实例对象。

根据原型链的三大规则,我们得到了原型链的基本结构图。
JS面向对象原理(二)------图解原型链(详)
那如果原型是另一个构造函数的实例呢?那就意味着这个原型本身内部有个__proto__指向另一个原型,相应地,另一个原型也有一个constructor指向另一个构造函数。这样就在实例和原型之间构造了一条原型链,这就是原型链的基本思想。查找实例属性时按照绿色的线路进行查找。
JS面向对象原理(二)------图解原型链(详)

原型搜索机制:原型链的存在拓展了原型的搜索机制,当访问实例对象的某个属性的时候,如果存在该属性,就返回该属性的值,否则,将自动查找该对象的__proto__ 指向的原型对象中是否存在该属性。如果存在,就返回该属性的值,否则,继续在原型对象的__proto__指向的原型对象中寻找。这样一直查下去,直到找到Object的原型对象,如果还没找到此属性,则返回undefined

其实原型链到这里也就应该差不多了,再加上JS的两个顶层构造函数也就该完整了。结合上段对Function和Object的关系阐述以及原型链的三大规则,得到了Function和Object的原型链图解:
JS面向对象原理(二)------图解原型链(详)
1.Function实例化了自己,所以Function是自己的实例,该实例的__proto__指向自己的原型对象。
2.Object实例化了自己的原型对象,所以Object.prototype是Object的实例,但是Object.prototype并没有指向自己是因为如果指向自己原型链将没有终点,原型链的搜索将会不断在Object.prototype中转圈圈,所以JS设定Object的原型对象的__proto__为null标志原型链的终点。
3.Function的原型对象是Object的实例,所以Function.prototype.__proto指向Object.prototype。
最后上张全家福—完整的原型链图:
JS面向对象原理(二)------图解原型链(详)

  • 下章讲解利用原型链可以做什么以及怎么做。

本文地址:https://blog.csdn.net/qq_34666266/article/details/109612696