DOJO中的面向对象__第五章 Dojo/_base/declare.js源码剖析(2)

接着第四章....

(二)   getInherited和inherited方法

  这两个方法是对外提供的扩展接口,可以用declare出来的类型的实例调用。它们的用途是返回父类中曾经出现的方法,或者对其进行调用。具体的用法在第二章中已经描述,这里不再举例说明。

  1. getInherited方法主要用来返回父类中的方法;

  2. inherited方法主要用来调用父类中的方法;

  首先来看getInherited方法,该方法的实现比较简单:

//getInherited的实现,调用了inherited
function getInherited(name, args){
	//如果传入name,则返回指定name的方法
	if(typeof name == "string"){
		return this.inherited(name, args, true);
	}
	//否则返回的是父类中的同名方法
	return this.inherited(name, true);
}

  上面的实现中可以看出,在getInherited中其实还是调用inherited来做进一步处理,只不过将inherited的最后一个参数设置为true,在inherited里会判断该参数,为true就是返回函数,不为true则是调用函数。

 

  接下来看inherited方法,可以分成个3步骤依次来看inherited具体的实现细节。第一步 依然是crack arguments,这是由于dojo提供的API的灵活性所致,就要求这样也能调用,那样也能调用,很蛋疼。第二步 就是根据name参数来寻找父类中的同名方法,这里的name有可能是constructor,也就是说我们可以利用inherited去调用父类中的构造函数,因此在inherited的实现里做了分情况讨论,一种情况是调用父类的非构造函数,还有一种是调用构造函数。想想在第三章末尾曾讲述过,可以把某继承链的构造器调用顺序设置为manual,这样将会破坏默认的自动调用父类构造函数,用户可以根据自己手动去调用父类的构造函数,用的正是this.inherited('constructor',arguments)....在第二步完成之后,第三步 所做的工作很简单,即决定是返回找到的同名方法还是调用这个同名方法。

function inherited(args, a, f){
	var name, chains, bases, caller, meta, base, proto, opf, pos,
		cache = this._inherited = this._inherited || {};	
	// 首先是crack arguments
	// 最后传入的参数f可能是true,也可能是一个替代args的数组,还有可能是默认的undefined
	if(typeof args == "string"){
		name = args;
		args = a;
		a = f;
	}
	f = 0;

	//args是子类方法的参数列表,args.callee代表在子类的哪个方法中调用了inherited
	caller = args.callee;	

	//获取欲调用的父类的方法的名称,如果没有传入name参数,那么就是调用父类中的同名方法
	name = name || caller.nom;
	if(!name){
		err("can't deduce a name to call inherited()");
	}
	//这里获取到的是子类型的meta信息,下面接着通过meta信息来进一步获取子类型的MRO链
	meta = this.constructor._meta;
	bases = meta.bases;

	//第一次调用inherited的时候,由于缺少this._inherited信息,
	//所以cache是一个空的Object,这里pos是undefined
	//但是如果第二回及以后用到了inherited
	//那么在cache中记录了之前一次利用inherited寻找的方法和位置
	//注意实际上整个实现中并未利用cache,这里的cache疑似某个实现版本遗留下的痕迹
	pos = cache.p;

	//分情况讨论
	//1.要调用的方法不是父类中的构造函数
	if(name != cname){
		// method
		if(cache.c !== caller){
			// cache bust
			pos = 0;
			base = bases[0];
			meta = base._meta;
			if(meta.hidden[name] !== caller){
				// error detection
				chains = meta.chains;
				if(chains && typeof chains[name] == "string"){
					err("calling chained method with inherited: " + name);
				}
				// find caller
				do{
					meta = base._meta;
					proto = base.prototype;
					if(meta && (proto[name] === caller && proto.hasOwnProperty(name) || meta.hidden[name] === caller)){
						break;
					}
				}while(base = bases[++pos]); // intentional assignment
				pos = base ? pos : -1;
			}
		}
		// 在正常情况下,在bases[0]中根据name寻找到的方法就是caller
		// 因此需要沿着bases继续寻找,有可能会进入while循环,找到的函数放在f中			
		base = bases[++pos];
		if(base){
			proto = base.prototype;
			if(base._meta && proto.hasOwnProperty(name)){
				f = proto[name];
			}else{
				opf = op[name];
				do{
					proto = base.prototype;
					f = proto[name];
					if(f && (base._meta ? proto.hasOwnProperty(name) : f !== opf)){
						break;
					}
				}while(base = bases[++pos]); // intentional assignment
			}
		}
		//这个写法太高级了....就是在bases中没有去就Object.prototype里找
		//不过很可能Object.prototype中依然没有名为name的方法,这时候f就是undefined
		f = base && f || op[name];
	}
	//2.要调用的方法是父类中的构造函数
	else{
		if(cache.c !== caller){
			//如果name是constructor,依然是沿着bases依次寻找
			pos = 0;
			meta = bases[0]._meta;
			if(meta && meta.ctor !== caller){
				// error detection
				chains = meta.chains;
				if(!chains || chains.constructor !== "manual"){
					err("calling chained constructor with inherited");
				}
				// find caller
				while(base = bases[++pos]){ // intentional assignment
					meta = base._meta;
					if(meta && meta.ctor === caller){
						break;
					}
				}
				pos = base ? pos : -1;
			}
		}
		// 这里找到的父类型的构造函数是base._meta.ctor
		// 即当初declare该类型时props中自定义的constructor函数
		while(base = bases[++pos]){	// intentional assignment
			meta = base._meta;
			f = meta ? meta.ctor : base;
			if(f){
				break;
			}
		}
		f = base && f;
	}

	// 把f和pos放进cache中,即this._inherited
	// 这样下次再次利用inherited寻找name方法的时候就很方便了
	cache.c = f;
	cache.p = pos;

	// 决定是返回f还是调用f
	if(f){
		return a === true ? f : f.apply(this, a || args);
	}
}

  上面不仅贴出了整个inherited的实现,也标注了一些关键步骤的解释。其中的cache疑似是一个过期的实现,因为实在想不出会有什么情况下cache.c === caller,除非人为的故意设置成如此,不过幸好即使忽略掉cache的作用也不会影响整段代码的理解。还有一个颇为有趣的地方在于如果发现了一个方法处于chains之中,那么会抛出异常,因为对一个已经chains的方法再去手动调用是毫无意义的。

(三)  isInstanceOf方法

  在第二章中已经提及,为了弥补JS自带的instanceof运算符无法判断Dojo中的继承,所以才有了isInstanceOf扩展。该方法由Dojo中类型的实例来调用。

function isInstanceOf(cls){
	//获取该类型meta信息中的bases(即MRO的结果)
	var bases = this.constructor._meta.bases;
	//遍历bases
	for(var i = 0, l = bases.length; i < l; ++i){
		if(bases[i] === cls){
			return true;
		}
	}
	return this instanceof cls;
}

  整个实现也很清晰很简单,在第三章曾经描述过,不论Dojo中一个继承结构多么的复杂,归根结底还是一个单继承的形式,外加mixin进了许多类型的属性而已。那么在判断instanceof的时候,只要顺着这样一条继承链从低向高处遍历,沿途无论是发现了mixin的class,或者直接就是父类,这里都会返回true。

 

如果对JS中原生instanceof的判断机制感兴趣,可以参考

1. http://www.iteye.com/topic/461096

2. http://ejohn.org/blog/objectgetprototypeof

 

(四)  chainedConstructor方法

  第四章中提过,在declare中构建ctor的时候,针对不同的情况分别调用了三个函数。这里只研究其中的chainedConstructor,因为该函数最为复杂,另外两个simpleConstructor和singleConstructor函数不作详细分析。chainedConstructor是当declare的类型存在继承,且未设置constructor="manual"时调用的函数,返回值是一个用以充当declare类型的函数。

 

  整个chainedConstructor可以看作三大步。第一步 就是就是执行在继承结构链上所有类型中定义的preamble函数,第二步 是调用所有类型的constructor函数,第三步 是执行当前类型中定义的postscript方法。如果撇开preamble和postscript,那么整个chainedConstructor的实现就只需要调用所有类型的constructor函数,相应的实现也可以被压缩成:

// 遍历bases中的类型,未指明constructor的调用顺序,因此默认是after,即
// 从bases[i]---->bases[0]依次调用constructor
for(i = bases.length - 1; i >= 0; --i){
	f = bases[i];
	m = f._meta;
	f = m ? m.ctor : f;//可能是raw class
	if(f){
		f.apply(this, arguments);
	}
}

 

  可惜必须要面对存在preamble和postscript的情况,而且这两种情况的处理方式还不一样。其实preamble的调用也很简单,就是遍历bases,找出其中定义过的preamble函数,然后依次执行。

function chainedConstructor(bases, ctorSpecial){
	return function(){
		var a = arguments, args = a, a0 = a[0], f, i, m,l = bases.length, preArgs;

		//如果不是利用new的时候调用该函数,那要强迫new出实例
		if(!(this instanceof a.callee)){
			// not called via new, so force it
			return applyNew(a);
		}

		// ctorSpecial=true仅发生在:没有定义chains,或者chains中的constructor
		// 一旦有了chains并且设置了constructor的顺序,则无需执行preamble
		// 有两种设置preamble的方式:
		// 一是在new的时候将preamble作为参数传递给该类型
		// 二是在declare一个类型的时候定义好
		if(ctorSpecial && (a0 && a0.preamble || this.preamble)){
			preArgs = new Array(bases.length);
			preArgs[0] = a;
			for(i = 0;;){
				// 如果是在参数中定义了preamble并传第给ctor
				a0 = a[0];
				if(a0){
					f = a0.preamble;
					if(f){
						a = f.apply(this, a) || a;
					}
				}
				// 如果是在类型的declare中定义preamble
				f = bases[i].prototype;
				f = f.hasOwnProperty("preamble") && f.preamble;
				if(f){
					a = f.apply(this, a) || a;
				}
				// for循环再遍历完了bases之后会结束
				if(++i == l){
					break;
				}
				//会记录下每次调用preamble之后的arguments
				//主要是为了防止某个preamble对arguments作出修改
				preArgs[i] = a;
			}
		}
		
		//第二步开始
		……

}

  上面对第一步preamble的执行作出了一些解释。现在还剩最后一步,只针对当前的类型来调用postscript:

f = this.postscript;
if(f){
     f.apply(this, args);
}

  这个实在没什么好讲的。

 

  从整个chainedConstructor的三大步骤实现来看,其实dojo的源码写的还是很通俗易懂的,结构也很清楚,是不错的学习材料^_^至此declare.js中比较重要的函数基本都已经讲完了,只缺少一个关于c3mro函数的剖析,但是前面讲mro已经花了大量的篇幅,便不打算再写下去了。以前都是仅仅停留在参阅Dojo的API的说明上,这是我第一次花力气去阅读Dojo的源码,可惜目前的工作中已经没有机会再使用Dojo了。

 

arcgis js api 3.X 加载WMTS服务,4326和3857 在arcgis js api 3.x版本中,加载geoserver发布的WMTS服务,坐标系包括4326、3857,需要对esri/layers/TiledMapServiceLayer进行扩展 1.4326坐标系, 设置切片的规则,包括比例尺、分辨率等,如果切片叠加存在偏移,在tileInfo中origin设置纠偏值 wmtsLayer.js define(["dojo/_base/declare", "esri/layers/TiledMapServiceLayer", ... 阅读详情

相关推荐

COLMAP生成MVSNet数据集

COLMAP生成MVSNet数据集

qq_43307074的博客 5637

arcgis for js 4.x加载时出现net::ERR_ABORTED 404与SSL问题

本地部署arcgis for js 4.x加载时出现net::ERR_ABORTED 404与SSL问题 问题原因 主要是由于arcgis for js 3.x使用的是http协议,arcgis for js 4.x使用的是https协议,需要配置SSL证书。 ...

weixin_39484674的博客 1293

Win11下高效管理JDK1.8与JDK17双环境的实战指南

本文提供了一份在Windows 11系统上高效管理JDK 1.8与JDK 17双环境的实战指南。文章详细讲解了如何实现双版本切换,包括如何干净安装两个JDK、通过巧妙配置环境变量实现命令行一键切换,以及在不同IDE和构建工具中为项目独立配置JDK版本,旨在帮助Java开发者彻底解决版本冲突问题,提升开发效率。

ipfs8storage的博客 544

ArcGIS JS API使用 百度高德 地图

本文参考 https://blog.csdn.net/weixin_41562754/article/details/79919924 1 定义图层类      1.1  定义百度图层类 define(["dojo/_base/declare", "esri/layers/TiledMapServiceLayer", "esri/geometry/Extent", "esri/Spati...

MZ_的博客 1846

EPICS Phoebus手册1

EPICS客户端phoebus 手册

yuyuyuliang00的博客 3954

AI大模型驱动Web UI自动化测试:Playwright与RobotFramework智能整合实践

Web自动化测试是软件质量保障的关键环节,其核心原理是通过脚本模拟用户操作,对Web应用界面进行功能验证。传统方法依赖固定的元素定位和脚本逻辑,在应对频繁的UI变更时面临维护成本高、脚本脆弱的挑战。随着AI大模型技术的发展,其强大的自然语言理解和代码生成能力为自动化测试带来了新的技术价值。通过将大模型的意图理解与代码生成能力,与Playwright强大的浏览器自动化执行引擎以及RobotFramework灵活的关键字驱动框架相结合,可以构建出能够理解自然语言指令、动态生成稳定操作脚本的智能测试系统。这种融合

weixin_28718769的博客 412

springboot项目 前端无法找到资源文件 net::ERR_ABORTED 404

问题描述 做一个springboot项目开发,导入jquery资源包时,出现net::ERR_ABORTED 404错误。确定过滤器,拦截器都放行js资源了,文件路径也没有出错。但是无论怎么重启服务器,怎么修改都没办法。 解决问题 这种情况,只可能是打包资源时,资源并没有打包进去。于是我打开target–>classes–>static下查找是否有该资源。果然,资源在打包时没有打包进去。我们只要在执行maven的clean命令再重启服务器即可。 ...

InterestAndFun的博客 1251

[AbutionGraph] 报表制作-使用Grafana动态可视化知识图谱指标

使用开源报表可视化工具Grafana实时监控时序知识图谱数据仓库AbutionGraph中的数据,实时性报表查询, 满足单次百万千万级的数据查询,毫秒级查询延迟,亚秒级对千亿数据量汇总统计,每日过亿报表查询/多维分析/日志分析等数据指标...

图特摩斯科技-博客 4278

前端报错net::ERR_ABORTED 404的解决方案

在IDEA项目中添加了新的前端资源,例如:CSS、JS等。在页面中对新资源进行引用时报错net::ERR_ABORTED 404。

谷哥的小弟 1万+

ANSYS 有限元分析 接触分析

目 录一、前沿二、接触单元类三、接触单元关键选项3.1 CONTA174 KeyOption3.2 TARGE170 KeyOption四、接触分析标准步骤4.1 定义实常数4.2 定义接触和目标单元4.3 创建接触和目标单元五、接触分析实例5.1 MPC5.2 标准接触六、尾声七、参考文献 阿阳的 ANSYS 使用指南,本文仅用于个人学习,除此之外,无其他任何用途。 ANSYS 有限元分析 概述 ANSYS 有限元分析 坐标系/工作平面 ANSYS 有限元分析 几何建模 ANSY

Hulunbuir 4万+

仿真学习系列(四十二):Sigrity仿真之走线阻抗和耦合检查

在高速电路设计中,走线的阻抗和信号线之间的耦合是影响信号完整性的重要因素。设计中可能会出现多种情况,导致阻抗变化或不匹配,例如线宽的变化、跨层分割等。传统的人工检查方法常常不够高效且易忽视细节。借助仿真软件,可以快速定位问题区域,并进行精确分析,从而有效避免或减少信号反射和串扰带来的影响。

案例文章,高速信号 425

ArcGIS是什么---kalrry---ing

ArcGIS是什么---kalrry---ing一、简介二、作用三、使用END、参考 一、简介 官网 ArcGIS 是一个全面的系统,用户可用其来收集、组织、管理、分析、交流和发布地理信息。作为世界领先的地理信息系统 (GIS) 构建和应用平台,ArcGIS 可供全世界的人们将地理知识应用到政府、企业、科技、教育和媒体领域。ArcGIS 可以发布地理信息,以便所有人都可以访问和使用。本系统可以在任何地点通过 web 浏览器、移动设备(例如智能手机和台式计算机)来使用。 二、作用 ArcGIS 使您可以 创

kalrry 2969

【花雕学编程】Arduino动手做(249)---ESP32+GC9A01之播放GIF的空战锁定动画

摘要: 本实验基于ESP32开发板,驱动1.28寸圆形IPS彩色TFT显示屏(GC9A01驱动,240×240分辨率),通过SPI接口实现GIF动画播放。利用TFT_eSPI和AnimatedGIF库,成功加载并循环播放预置的科幻风格GIF动画(如HUD界面、X翼战机等)。代码实现了GIF解码、逐帧渲染及动态显示功能,通过串口调试监控运行状态。实验验证了该显示屏在动态图像显示中的流畅性,为嵌入式图形应用提供了实用方案,适用于智能穿戴、交互设备等场景。 关键词: ESP32、GC9A01、圆形TFT、GIF播

雕爷学编程 492

DOJO中的面向对象__第四章 Dojo/_base/declare.js源码剖析(1)

  declare.js中包含了整个dojo面向对象中最重要的代码,即对类型表达和扩展的一些封装。功能虽然强大,但是幸好文件并不复杂,拥有清晰的脉络。整个declare.js一共定义了15个函数,14个具名函数,1个匿名函数。这14个具名函数中又有一些是declare.js内部使用的函数,外部无法调用,还有一些是由dojo提供的接口,可以供dojo.declare声明的类型来调用。具体函数如下所示...

driftcloudy的专栏 251

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Dojo

dojo的类机制支持类声明、继承、调用父类方法等功能。dojo在底层实现上是通过操作原型链来实现其类机制的,而在实现继承时采用类式继承的方式。值得一提的是,dojo的类机制允许进行多重继承(注意,只有父类列表中的第一个作为真正的父类,其它的都是将其属性以mixin的方法加入到子类的原型链中),为解决多重继承时类方法的顺序问题,dojo用JavaScript实现了Python和其它多继承语...

dianxuanfan4560的博客 337

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Dojo 1.9.2 js压缩

1.首先下载dojo 1.9.2的 source , 解压后一共四个文件夹: dojo,dijit,dojox,util 2.在util\buildscripts\profiles文件夹中新建文件 mydojo.profile.js dependencies = { basePath: "../../../", // releaseDir: "erms"

dyf7970268的专栏 1113

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

蛮不错的Dojo学习资源

[size=medium]第一章 JS中的对象模型[/size] http://driftcloudy.iteye.com/blog/907177 [size=medium]第二章 Dojo中的类[/size] http://driftcloudy.iteye.com/blog/907697 [size=medium]第三章 dojo中的多继承[/size] http://driftcl...

j2ee_zhongqi的专栏 127

[dojo源码学习]环境加载

dojo的文档实在不敢恭维,学习dojo,我个人认为读源码是个比较好的方式。 首先这个引用,你懂的 &lt;script type="text/javascript" src="dojo/dojo/dojo.js"&gt;&lt;/script&gt; 故事就是这么开始的,dojo就这么被用上了   dojo.js打开dojo.js,一大段注释就映入眼帘了...(dojotoolkit...

分享 166

dgrid技术深度解析:现代Web数据网格的架构设计与性能优化

在当今数据驱动的Web应用中,高效的数据展示与交互成为前端开发的核心挑战。dgrid作为基于Dojo Toolkit的轻量级数据网格组件库,专注于解决大规模数据集的可视化问题。其技术定位在于提供模块化、高性能的数据网格解决方案,特别针对现代Web应用的实时数据更新、虚拟滚动和移动端适配等关键需求。dgrid的核心价值体现在其与dstore数据层的深度集成,实现了数据操作与UI渲染的彻底解耦,为复杂

gitblog_00584的博客 350

net::ERR_ABORTED 404 (Not Found)错误

今天开发Web,在引用静态资源的时候出现了net::ERR_ABORTED 404 (Not Found)这个错误。记录一下。 1、首先介绍一下我的项目的目录结构: 2、然后我原来的的引用js文件的代码是: 因为在eclipse中点击src中的路径可以跳转到对饮的js文件中,所以我一直认为我的引用路径的方式没有问题。所以对于出现的net::ERR_ABORTED 404 (Not F...

sinat_41367326的博客 7万+

(3)Dojo学习之Class

引言 dojo中的类 1创建一个简单的类 2类和模块结合使用 3关于类的继承 需求 1需求一 2需求二很重要 21代码实现 22程序出错 23程序运行 3 需求三 再次介绍require加载模块 总结 1本博客代码的下载地址dojo_class1.引言      说到面向对象,我们就不得不提一下类,在原生的js中我们是不可以创建类的,没有class这个关键字,但是在dojo中,dojo自定义了一个模

王培晓Carpenter 4158

DOJO 基本原理 之 dojo/_base/declare<7>

在Dojo 工具箱中, dojo/_base/declare模块是创建类的基础。 declare允许开发者实现类多继承,从而创建有弹性的(灵活的)代码, 避免写重复的代码。 Dojo, Digit, Dojox模块都使用了declare.  在这个指南中,你将知道你为什么也需要它。 开始 在开始之前你需要先看看 modules 指南 . Dojo类创建的基础 declare 函

robin xiong的专栏 3063

dojo 三 类和继承 dojo/_base/declare

这里要讲有关类的定义、继承和实现。官方教程:http://dojotoolkit.org/documentation/tutorials/1.7/declare/类的声明是通过declare 这个方法来实现的。define(['dojo/_base/declare'],function(declare){//第一个declare,声明类my.A  declare('my.A',null,{  ...

weixin_34378922的博客 359

dojo的API功能介绍

文章目录一、dojo常见API1、dojo/_base/declare(1)作用:该模块用于声明一个类,类似于js 的class的作用。(2)用法:declare(className, superClass, properties)(3)案例(4)参考链接2、dojo/_base/lang(1)作用:就是一个扩展模块,提供了一些有用的方法(2)用法:部分函数说明(3)案例(4)参考链接3、dojo/_base/array(1)作用:封装了数组中的一些方法。类似于JS当中数组(2)用法:跟JS的数组的方法使用

Litt_White的博客 2840

Dojo学习笔记(六):dojo/_base/declare

1、JavaScript 继承方式 (1)对象冒充 在 JavaScript 中,构造函数也和普通的函数一样,可以被赋值和调用,对象冒充通过此原理来模拟继承。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 3...

weixin_34413103的博客 267

从零开始学_JavaScript_系列(17)——dojo(6)(声明一个类declare)

(71)声明一个类declare 作用: 封装一个类,然后可以通过调用这个类,来调用其类方法。   优点: ①可以继承其他类的类方法;   ②假如我们需要使用一个类的类方法,而这个类依赖于5个插件。如果正常调用的话,我们需要在每个使用这个类的js文件里,调用这5个插件。当我们使用封装时,我们只需要调用这个类即可。而调用这个类的方法很简单。   ③可以使得代码模块化,按需加载。

qq20004604的博客 8832
上一篇: DOJO中的面向对象__第四章 Dojo/_base/declare.js源码剖析(1)
下一篇: JS中的对象创建、属性访问
driftcloudy
博客等级 码龄16年 1粉丝 1原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值