工具
您可以使用一些JavaScript方法自己创建这个树。在这种方法中,我将使用地图
很多,因为它允许我们轻松地将任意值相互映射(即键不仅仅是字符串和符号,就像对象中的一样),以及设置
.
获取原型
JavaScript中的继承通过对象的内部原型工作。可以通过以下方式进行观察对象.get原型
.这个原型
派生构造器(函数)的属性是基本构造器的实例(函数);及其原型
属性是原型链的下一步。
这些关系阐明了这一点:
Object.getPrototypeOf(Node.prototype)===EventTarget.protoype//节点从EventTarget原型继承属性(EventTarget是节点的超类)。Object.getPrototypeOf(EventTarget.prototype)===Object.protoype//EventTarget从对象原型继承属性(Object是EventTarget的超类)。Object.getPrototypeOf(Object.prototype)===null//对象不从任何东西继承属性(Object是基类)。
请注意,构造函数的继承行为可能会产生误导,并且不是我们要使用的:
Object.getPrototypeOf(Node)===EventTarget//这行得通,不是吗?Object.getPrototypeOf(EventTarget)===Function.prototype//函数不是EventTarget的超类;这只是构造函数的基础,构造函数是一个函数。Object.getPrototypeOf(Object)====Function.prototype//这里只显示Function,因为构造函数是它的实例。
当尝试读取对象报告的内部原型时,原型链结束无效的
,在Web浏览器中,最初只发生在Object.getPrototypeOf(Object.prototype)
.这适用于所有内置和主机定义的构造函数,除了代理
,它没有原型
属性,尽管它是构造函数。它不需要这样做的原因是代理“实例”(即。新代理(目标,处理程序)
)使用构造时获取第一个参数(代理目标)的原型新的
.我们暂时不讨论这个问题。
获取所有类
由于大多数内置和主机定义的构造函数都是全局的,因此可以获取所有构造函数,但以下情况除外类型数组
.使用Object.getOwn属性描述符
生成所有全局属性及其描述符。(在网上,窗口
可以代替全球This
,在Node上,它是全球的
.)
描述符包含一些设置,例如,如果可以在对于
–在里面
环路等。如果属性是getter/setter对,您将看到相应的得到
和设置
功能。任何正常属性都具有价值
描述符。没有构造函数是getter/setter对,因此价值
必须存在,并且由于施工人员是全球性的财产,我们正在寻找功能.如前所述,这些构造函数必须具有原型
属性,或是代理
.
Object.entries(Object.getOwnPropertyDescriptors(globalThis)).filter(([_,{value}])=>value===Proxy||typeof value===“function”&&value.hasOwnProperty(“prototype”)
这将获得所有构造函数的列表,但由于代理
是一种特殊情况,并且对象
有一个讨厌的“Null Prototype”要处理,让我们实际过滤掉它们并手动处理它们。
const allConstructors=Object.entries(Object.getOwnPropertyDescriptors(globalThis)).filter(([_,{value}])=>值!==Object&&typeof value===“function”&&value.hasOwnProperty(“prototype”);
生成树
我们将初始化三个地图
秒:
类继承树
是具有继承结构的树。
类继承引用
是一个平面结构,将每个构造函数映射到中的引用类继承树
.
构造函数名称
将每个构造函数映射到与其关联的任何名称。
const classInheritanceTree=新映射([[无效的,新建地图([[对象,新建地图()]])],]),classInheritanceReferences=新映射([[null,classInheritanceTree.get(null)],[对象,classInheritanceTree.get(null).get(对象)]]),constructorNames=新映射([[无效的,新建集合([“空”])],[对象,新建集合([“对象”])]]);
当然,无效的
不是真正地继承树的一部分,但出于可视化目的,它充当有用的树根。请注意.构造函数.名称
不总是与上的属性名称匹配全球This
,例如在Firefox 90中:webkitURL.name===“URL”
和网络套件CSSMatrix.name===“DOMMatrix”
、以及webkitURL===URL
和WebKitCSSMatrix===DOMMatrix
.这就是为什么构造函数名称
是设置
包含所有别名的。
我们通过迭代所有构造函数并确定建造师
他们的原型。的自我调用populateInheritanceTree
函数只确保超类存在于地图
s,然后将其子类放入结构中。类继承树
仅隐式填充为类继承引用
填充:后者包含对地图
在前一个中,所以通过更新一个,我们也改变了另一个。
allConstructors.forEach(函数populateInheritanceTree([name,{value}]){const superClass=Object.getPrototypeOf(value.prototype).constructor;//确保超类包含在`classInheritanceReferences`;中;//使用与超类对应的参数调用函数本身。if(!classInheritanceReferences.has(superClass)){填充命名树([超级类名称,{值:superClass}]);}//如果尚未包含该类,请在“classInheritanceReferences”中放置引用`//并隐式放入`classInheritanceTree`(通过`.get(superClass)`)。//这两个Map值引用相同的Map引用:`subClasses`。if(!classInheritanceReferences.has(value)){const subClasses=新映射();类继承引用.set(值,子类).get(超类).set(值,子类);}//为所有名称和别名创建集合。if(!constructorNames.has(value)){constructorNames.set(value,new set());}//添加属性名称。constructorNames.get(值).add(名称);//如果构造函数的“name”属性存在(可能不同),请添加该属性。if(value.name){constructorNames.get(值).add(value.name);}});
可视化树
一旦我们有了类继承树
,让我们把它们放入<ul>
–<li>
结构。我们将添加一个数据崩溃
属性来跟踪哪些元素是可展开的,哪些元素是展开的,而哪些元素是折叠的。
const visualizeTree=(map,names)=>Array.from(map).map(([构造函数,subMap])=>{const listItem=document.createElement(“li”),listItemLabel=document.createElement(“span”);listItemLabel.append(…Array.from(names.get(constructor)).flatMap((textContent)=>[Object.assign(document.createElement(“代码”){text内容}),", "]).切片(0,-1);listItem.append(listItemLabel);if(subMap.size){const-subList=document.createElement(“ul”);listItem.setAttribute(“data-collapsed”,“false”);listItem.append(subList);subList.append(…可视化树(subMap,名称));}return listItem;});document.body.appendChild(document.createElement(“ul”)).append(…visualizeTree(classInheritanceTree,constructorNames));
我们按字母顺序对列表项目进行排序,但首先列出可扩展的项目。剩下的只是一些UI处理和CSS…
所有构造函数的树(除了代理
)在Web上公开(正常的浏览器上下文,而不是Worker)
此代码将前面的所有步骤放在一起。单击每个可展开的项目以展开或折叠它。底部还有一张结果图片。
然而,我知道您已经询问过有关Web API或DOM API的问题。这些很难自动隔离,但希望现在已经有所帮助。
读者练习:自动包含树中每个名称的MDN链接。
“使用严格”;const allConstructors=Object.entries(Object.getOwnPropertyDescriptors(globalThis)).filter(([_,{value}])=>值!==Object&&typeof value===“function”&&value.hasOwnProperty(“prototype”)),classInheritanceTree=新映射([[无效的,新建地图([[对象,新建地图()]])]]),classInheritanceReferences=新映射([[null,classInheritanceTree.get(null)],[对象,classInheritanceTree.get(null).get(Object)]]),constructorNames=新映射([[无效的,新建集合([“空”])],[对象,新建集合([“对象”])]]),visualizeTree=(map,names)=>Array.from(map).map(([构造函数,subMap])=>{const listItem=document.createElement(“li”),listItemLabel=document.createElement(“span”);listItemLabel.append(…Array.from(names.get(constructor)).flatMap((textContent)=>[Object.assign(document.createElement(“代码”){text内容}),", "]).切片(0,-1);listItem.append(listItemLabel);if(subMap.size){const-subList=document.createElement(“ul”);listItem.setAttribute(“data-collapsed”,“false”);listItem.append(subList);subList.append(…可视化树(subMap,名称));}return listItem;}).sort((listItemA,listItemB)=>listItemB.hasAttribute(“data-collapsed”)-listItemA.hasAttribute;allConstructors.forEach(函数populateInheritanceTree([名称,{值}]){const superClass=Object.getPrototypeOf(value.prototype).constructor;if(!classInheritanceReferences.has(superClass)){populateInheritanceTree([超级类名称,{值:superClass}]);}if(!classInheritanceReferences.has(value)){const-subClasses=新映射();类继承引用.set(值,子类).get(超类).set(值,子类);}if(!constructorNames.has(value)){constructorNames.set(value,new set());}constructorNames.get(值).add(名称);if(value.name){constructorNames.get(值).add(value.name);}});document.body.appendChild(document.createElement(“ul”)).append(…visualizeTree(classInheritanceTree,constructorNames));addEventListener(“点击”,({target})=>{if(target.closest(“span”)&&target.losest((“li”).hasAttribute(“data-collapsed”)){target.closest(“li”).setAttribute(“data-collapsed”,JSON.stringify(!JSON.parse(target.crosest(”li“).getAttribute(”data-collabsed“)));}});
ul公司{向左填充:2em;}李{向左填充:.3em;列表样式类型:光盘;}li[data-collapsed]>span{光标:指针;}li[data-collapsed]>span:悬停{背景:#ccc;}li[data-collapsed='false']{列表样式类型:“▼”;}li[data-collapsed='true']{列表样式类型:'▶';}li[data-collapsed='true']>ul{显示:无;}
这是我的Firefox Nightly 90.0a1.