ArcGIS Maps SDK中的Accessor

Accessor是ArcGIS Maps SDK for JavaScript中绝大多数类型的基类。通过Accessor类型的封装,为SDK中提供了响应式类型的基础。

Accessor由多个不同层次的代码所组成,并设计了复杂的逻辑结构,提供除响应式更新之外的多种不同的特性,让我们从用户所接触的Accessor类开始。

esri/core/Accessor


位于esri/core(或者@arcgis/core/core)命名空间中的Accessor类型实际并不直接提供响应式的能力,他是一层包装类,目的是提供一致的外层API,以及定义生命周期,这里实际上是一个适配器,实现了OO编程中经典的适配器模式来隔离变更。SDK在当前4.28版本之前的多个版本中实际对于底层的Accessor实现进行了多次的优化,乃至于重构,但对于使用者而言,这完全是无感的。

JavaScript 复制代码
class Accessor {
	static createSubclass(args) {}

	constructor(...args) {}

	postscript(ctorArgs) {}

	initialize() {}

	destroy() {}

	commitProperty(p) {}
	get(path) {}
	set(path, value) {}

	hasOwnProperty(p) {}
	keys() {}

	watch(path, handler, opts) {}
	addHandles() {}
	removeHandles(h) {}
	removeAllHandles() {}
	hasHandles() {}

	notifyChange(property) {}
}

如果我们将以上的Accessor类型接口定义进行分组,我们可以梳理得到Accessor所承担的3个职责,分别是:

  1. 生命周期定义:constructorpostscriptinitializedestroy
  2. 响应式代理/适配器:getsetcommitProperty
  3. 观察者模型(类似RxJS中的Subject):watchnotifyChange

另外,新版本中为了更好的管理Accessor对象,还额外为Accessor类型新增了状态机制。

这就是ArcGIS Maps SDK for JavaScript中强大的Accessor。

我们从构造函数开始深入了解Accessor类型。

JavaScript 复制代码
const accessorHandlesSymbol = Symbol("Accessor-Handles")
const accessorInitialized = Symbol("Accessor-Initialized")

class Accessor {
	constructor(...args) {
		// Step 1: 初始化状态机
		this[accessorHandlesSymbol] = null;
		this[accessorInitialized] = false;

		// 禁止直接创建Accessor基类型的实例
		if (this.constructor === Accessor) {
			throw new Error("[accessor] cannot instantiate Accessor. This can be fixed by creating a subclass of Accessor");
		}

		// Step 2: 创建响应式属性代理对象
		const properties = new esri.core.accessorSupport.Properties(this);
		Object.defineProperty(this, "__accessor__", { 
			enumerable: !1, 
			value: properties,
		})


		// Step 3: 为继承类型预留的统一参数格式化处理接口
		if(args.length > 0 && this.normalizeCtorArgs) {
			properties.ctorArgs = this.normalizeCtorArgs.apply(this, args);
		}
	}
}

由于Accessor作为基类,预定义了大量的空方法,约等同于Java等高阶语言中的抽象类,不允许直接实例化。作为统一的API,在Accessor类型之上提供了静态的函数继承入口。

JavaScript 复制代码
class Accessor {
	static createSubclass(typeProps = {}) {
		/* 在旧版本中,Accessor曾经基于dojo来定义,因此能支持多重继承
		*  随着新版本完全移除了dojo,为了兼容TypeScript的类型机制,不再允许多种继承
		*/
		if (Array.isArray(typeProps)) {
			throw new Error("Multi-inheritance unsupported since 4.16");
		}

		// 解构类型描述中关于继承类的定义型属性并与其他属性剥离
		const { properties: t, declaredClass: e, constructor: s } = typeProps;
		delete r.declaredClass;
		delete r.properties;
		delete r.constructor;

		// 生成继承类型
		const o = this;
		class i extends o {  
		  constructor(...r) {  
			super(...r);
			(this.inherited = null);
			 s && s.apply(this, r);  
		  }  
		}

		// 解析继承类型属性元信息
		getPropertiesMetadata(i.prototype);

		for (const c in r) {
			// 逐个解析传入的其他属性元信息
			// 此处省略
		}

		for (const c in t) {
			// 解析初始化传递的属性定义并构建Property代理对象
			// 此处省略
		}

		return subclass(e)(i)
	}
}

subclass作为一个类装饰器,为继承的子类型清扫了JavaScript语言层面的最后障碍,并补充了类型定义关键信息,最后将属性元信息__accessorMetadata__与属性代理__accessor__进行关联,构成了Accessor类型的最后一块拼图。

让我们继续深入了解Accessor如何构建SDK的基础。

如果你觉得本文对你有些许启发,请持续关注我的公众号"戈伊星球"吧!

相关推荐
IT女孩儿1 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡2 小时前
commitlint校验git提交信息
前端
虾球xz2 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇2 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒2 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员3 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐3 小时前
前端图像处理(一)
前端
程序猿阿伟3 小时前
《智能指针频繁创建销毁:程序性能的“隐形杀手”》
java·开发语言·前端
疯狂的沙粒3 小时前
对 TypeScript 中函数如何更好的理解及使用?与 JavaScript 函数有哪些区别?
前端·javascript·typescript
瑞雨溪3 小时前
AJAX的基本使用
前端·javascript·ajax