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的基础。

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

相关推荐
懒懒是个程序员17 分钟前
layui时间范围
前端·javascript·layui
NoneCoder19 分钟前
HTML响应式网页设计与跨平台适配
前端·html
凯哥197023 分钟前
在 Uni-app 做的后台中使用 Howler.js 实现强大的音频播放功能
前端
烛阴25 分钟前
面试必考!一招教你区分JavaScript静态函数和普通函数,快收藏!
前端·javascript
GetcharZp28 分钟前
xterm.js 终端神器到底有多强?用了才知道!
前端·后端·go
JiangJiang31 分钟前
🚀 React 弹窗还能这样写?手撸一个高质量 Modal 玩起来!
前端·javascript·react.js
吃炸鸡的前端1 小时前
el-transfer穿梭框数据量过大的解决方案
前端·javascript
高德开放平台1 小时前
文末有奖|高德MCP 2.0 出行领域首发打通大模型与高德地图APP互联
前端
苳烨1 小时前
UniApp使用最新版Android Studio本地离线打包全流程
前端
Monly211 小时前
vue报错:Loading chunk * failed,vue-router懒加载出错问题。
前端·javascript·vue.js