我理解的DOM文档对象模型,说白了就是一系列用于表示文档的东西,这些东西本质是对象,这些对象表示了文档的各个不同部位。
例如DOM中DOM对象也叫DOM节点,节点有分document节点、元素节点、属性节点、文本节点、注释节点等,分别表示文档中的整个文档、某个元素、某个元素的属性、某个元素里面的文本、某个元素里面的注释等。
节点对象中定义了一系列的属性和方法,这些属性和方法跟我们平时自己写的js并没有什么不同,只不过这一系列的接口由浏览器厂商定义,用于更好、更方便的表示和操作文档。
而DOM本质就是这一系列对象的继承关系,由于这继承关系复杂,导致我们不好理解。特写这边文章梳理一下,看看DOM里面到底有什么鬼!
那从哪里开始看呢?
没有思路的话,就以我们熟悉的document为例子吧
document对象事代表整个HTML文档的,我们看看它的原型及它是谁构造出来的
可以看到document对象是由构造函数HTMLDocument(){}构造出来的,该构造函数的原型是HTMLDocument{}
document可以继承其原型HTMLDocument{}上的属性和方法
不妨一探究竟,看看原型里面到底定义里什么,看有没有我们眼熟的。
发现HTMLDecument.prototype上居然定义了怎么多属性和方法,我画了一下上篇文章有讲到的一些用于遍历节点的方法。
看看目前的关系:
接着,我们来看看
- 构造函数HTMLDocument(){}
- 原型对象HTMLDocument{}
发现
- HTMLDocument(){}的构造函数Function(){},我们知道所有方法都是都new Function产生的,所以这里已经到尽头,无需说再往下看Function(){}
- HTMLDocument(){}作为对象时,它的原型
__proto__
是Document(){};而它作为function时,原型指向是HTMLDocument:{},这里别搞混了。
接着来看,原型对象HTMLDocument{}即document.__proto__
image.png
发现
document.__proto__
的构造函数是HTMLDocument(){};这就是上面一直说的那个HTMLDocument(){}。
这点其实很容易理解,构造函数上有个prototype属性指向原型,原型有个属性constructor指向该构造函数
还发现:
document.__proto__
往上,即:document.__proto__.__proto__
是继承自Document{}
我们在来看看这个原型对象里面有什么鬼:
我去,这都是什么鬼
有空可以点开认真看看,其实我想主要表达的是:为什么document能使用getElementsByClassName这些东西,其实就是因为它顺着它的原型链往上找,找到这里定义的getElementsByClassName方法。
我在这里小结一下吧:
1. document并非由Document(){},而是由HTMLDocument(){}构造
2. document的原型是HTMLDocument:{},这个HTMLDocument对象里面定义了很多方法和属性,如:
children、childNodes、lastChild、lastElementChild、nextSibing、nodeName、nodeType、nodeValue、parentElement、parentNoded等等
3. document的原型的原型是Document:{}。这个大的Document上方法和属性巨多,忍不住想说几个方法:
crateElement()、crateTextNode()、crateAttribute()、getElementById()、getElementsByClassName()、getElementsByName()、getElementsByTagName()、querySelector()、querySelectorAll()等等...
到这里可以在看下我们的图,捋一下他们的关系:
看到这里有心细的同学肯定会想Document:{}和Document(){}是不是有什么关系呢?
你猜的没错,Document:{}就是这个Document(){}构造出来的,不信我,那咱们试试:
哈哈!
go on!
我再来看Document:{}的原型是谁
为了足够深入,我们还是得看看它指向的Node:{}里面又有些什么(别怕麻烦,一次搞清楚了,下次再也不怕了)
我们发现Node里面也有一些的方法和属性,常见的如:
hasChildNodes()
另外有好多数值,视乎是nodeType;而且它还有原型指向EventTarget:{}
为了不乱,先画下图吧
那我先看EventTarget:{}吧,我们就是想看看document最终继承自谁,还没完没了了不成。
看看EventTarget:{}里面的东西:
这下开心了,终于看到了想看到的东西,就是我们最最熟悉的Object了
另外还发现原来给元素添加和移除事件的方法
addEventListener()、removeEventListener()
也定义在EventTarget:{}里头了试试Object上面还有吗?
Object就到顶了,再上就会返回null。那这里就不用再看了。
document的原型链
接着我们回到图看下先
我们先看看EventTarget:{}和Object:{}分别是谁构造出来的吧:
发现EventTarget:{}和Object:{}分别是由对应的构造函数EventTarget(){}和Object(){}构造出来的。
接着在来看Document(){}、EventTarget(){}、Object(){}是谁构建出来的
发现所有的function都是由function Function(){}构建出来的
那Function的构造函数呢?也会返回function Function(){}!!!
js中一切皆是对象,我们看看Function(){}作为对象时,它的原型指向是谁
第一层指向匿名function(){},第二层就直接指向Object了。
其中我这里可以看到一个规律:
一般原型对象(非原型链首尾对象)都是两进两出,即:
两进是别的对象__proto__指向我和构造函数prototype指向我;
两出是我的属性constructor和__proto__
先完善这点
再看,一般的方法是两进三出:
两进是我生产的对象的constructor指向我,以及我的原型的constructor指向我。(这里大部分没讲到自己生产的对象,所以只有一个原型指向我)
三出是:1 我的constructor指向Function,2我作为function我的prototype指向谁,3我作为对象时我的
__proto__
指向谁规律非绝对!
根据上面说的规则再来完善,另外发现上面漏了一个Node:{},现在补上,对不住大家了哈:
我们发现:
基本所有的对象都是通过构造函数产生的,而所有的构造函数都是Function产生的
基本所有对象的原型都是最终继承自Object。
到这里其实也还没讲完,这是一个思路!这只是以document为例子讲的。因为DOM对象太多,他们继承的情况有所不同,大家可以根据这个思路将你想了解的元素类型,通过这个方式更加深入了解到其里面本来面目,让学习学的更加透彻!!
网友评论