TypeScript 自定义装饰器

(预测未来最好的方法就是把它创造出来——尼葛洛庞帝)

在这里插入图片描述

装饰器

装饰器一种更现代的代码模式,通过使用@的形式注入在属性,寄存器,方法,方法参数和类中,比如在Angular,Nestjs和midway等流行框架中也都用到了装饰器。
官方说明
对装饰器更详细的相关说明

tc39提案

tc39提案一共分为五个阶段

Stage 0 - 设想(Strawman):只是一个想法,可能有 Babel插件。
Stage 1 - 建议(Proposal):这是值得跟进的。
Stage 2 - 草案(Draft):初始规范。
Stage 3 - 候选(Candidate):完成规范并在浏览器上初步实现。
Stage 4 - 完成(Finished):将添加到下一个年度版本发布中。

装饰器目前已经在阶段3,相信不久的将来,js中也会支持装饰器。在typescript5.0中已经完全支持了装饰器。
该提案进度更详细的相关说明

装饰器实践

tsconfig.json

{"compilerOptions": {"target": "ES6", // 为发出的JavaScript设置JavaScript语言版本,并包含兼容的库声明"experimentalDecorators": true, // 启用对遗留实验装饰器的实验支持"module": "ES6", // 指定生成的模块代码"esModuleInterop": true, // 发出额外的JavaScript以简化对导入CommonJS模块的支持。这为类型兼容性启用了“allowSyntheticDefaultImports”"moduleResolution": "node", // 指定TypeScript如何从给定的模块说明符中查找文件"outDir": "dist", // 为所有发出的文件指定输出文件夹"rootDir": "src", // 指定源文件中的根文件夹},"include": [ // 需要编译的目录文件"src/**/*",],"exclude": [ // 需要排除的目录文件"node_modules"]
}

官方更详细的配置说明

类装饰器

typescript

// 通过装饰器对类进行扩展,减少对代码侵入性和业务间的耦合性
// constructor参数为类的构造函数
const classExtends = () => {const ClassDecorator = (constructor: Function) => {console.log('ClassDecorator---');// 扩展类属性constructor.prototype.name = 'ClassDecoratorName';// 扩展类方法constructor.prototype.run = () => {console.log('ClassDecorator run test');}};interface Test {name: string;run (): void;}@ClassDecoratorclass Test {}new Test().run();const obj = { name: 'adsa' };Reflect.get(obj, 'name');
};
classExtends();// 通过装饰器入参的形式对类进行扩展,使用参数可以对业务进行更强的定制化处理
const classExtendByParams = () => {const ClassDecorator = (param: string) => {return function (constructor: Function) {// 针对入参做不同的处理constructor.prototype.run = () => {if (param === 'agent') {console.log('classExtendByParams run agent');} else if (param === 'user') {console.log('classExtendByParams run user');}};}};interface Test {name: string;run (): void;}@ClassDecorator('agent')class Test {}new Test().run();
};
classExtendByParams();// 通过装饰器工厂方法进行扩展,工厂方法装饰器可以和类型更好的兼容
const classExtendOfFactory = () => {const ClassDecorator = (param: string) => {return function <T extends new (...args: any[]) => any> (constructor: T) {return class extends constructor {run () {if (param === 'agent') {console.log('classExtendOfFactory run agent');} else if (param === 'user') {console.log('classExtendOfFactory run user');}};}}};const Test = ClassDecorator('user')(class { });new Test().run();
};
classExtendOfFactory();

javascript

var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;return c > 3 && r && Object.defineProperty(target, key, r), r;
};
// 通过装饰器对类进行扩展,减少对代码侵入性和业务间的耦合性
// constructor参数为类的构造函数
const classExtends = () => {const ClassDecorator = (constructor) => {console.log('ClassDecorator---');// 扩展类属性constructor.prototype.name = 'ClassDecoratorName';// 扩展类方法constructor.prototype.run = () => {console.log('ClassDecorator run test');};};let Test = class Test {};Test = __decorate([ClassDecorator], Test);new Test().run();const obj = { name: 'adsa' };Reflect.get(obj, 'name');
};
classExtends();
// 通过装饰器入参的形式对类进行扩展,使用参数可以对业务进行更强的定制化处理
const classExtendByParams = () => {const ClassDecorator = (param) => {return function (constructor) {// 针对入参做不同的处理constructor.prototype.run = () => {if (param === 'agent') {console.log('classExtendByParams run agent');}else if (param === 'user') {console.log('classExtendByParams run user');}};};};let Test = class Test {};Test = __decorate([ClassDecorator('agent')], Test);new Test().run();
};
classExtendByParams();
// 通过装饰器工厂方法进行扩展,工厂方法装饰器可以和类型更好的兼容
const classExtendOfFactory = () => {const ClassDecorator = (param) => {return function (constructor) {return class extends constructor {run() {if (param === 'agent') {console.log('classExtendOfFactory run agent');}else if (param === 'user') {console.log('classExtendOfFactory run user');}};};};};const Test = ClassDecorator('user')(class {});new Test().run();
};
classExtendOfFactory();

方法装饰器

typescript

/*** 入参解释* target: 对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象* propertyKey: 属性的名称* descriptor: 属性的描述器*/
/*** PropertyDescriptor参数解释* PropertyDescriptor的参数各项为js的属性描述符,在创建变量或方法等对象时,js会默认赋予这些描述符* 详细的阅读https://www.tektutorialshub.com/javascript/javascript-property-descriptors-enumerable-writable-configurable/* descriptor 参数* value 方法自身* writable 该方法是否可以被变更* enumerable 是否可以被枚举* configurable 决定是否可配置,如果为false,则value,writable,enumerable不能被修改*/// 通过装饰器对方法进行扩展,减少对代码侵入性和业务间的耦合性
const methodExtends = () => {const MethodDecorator = (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {// 获取方法本身const method = descriptor.value;// 对该方法的生命周期进行操作descriptor.value = (...args) => {console.log('MethodDecorator before run');const data = method.call(this, args);console.log('MethodDecorator after run');return data;};};class Test {@MethodDecoratormethod () {return ';;;;';}}console.log(new Test().method());
};
methodExtends();// 通过装饰入参的形式对方法进行扩展,使用参数可以对业务进行更强的定制化处理
const methodExtendsByParams = () => {const MethodDecorator = (param: string) => {console.log('methodExtendsByParams', param);return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {// 获取方法本身const method = descriptor.value;// 对该方法的生命周期进行操作descriptor.value = (...args) => {console.log('before run');const data = method.call(this, args);console.log('after run');return data;};}};class Test {@MethodDecorator('user')method () {return ';;;;';}}console.log(new Test().method());
};
methodExtendsByParams();

javascript

/*** 入参解释* target: 对于静态成员来说是类的构造函数,对于实例成员来说是类的原型对象* propertyKey: 属性的名称* descriptor: 属性的描述器*/
/*** PropertyDescriptor参数解释* PropertyDescriptor的参数各项为js的属性描述符,在创建变量或方法等对象时,js会默认赋予这些描述符* 详细的阅读https://www.tektutorialshub.com/javascript/javascript-property-descriptors-enumerable-writable-configurable/* descriptor 参数* value 方法自身* writable 该方法是否可以被变更* enumerable 是否可以被枚举* configurable 决定是否可配置,如果为false,则value,writable,enumerable不能被修改*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;return c > 3 && r && Object.defineProperty(target, key, r), r;
};
// 通过装饰器对方法进行扩展,减少对代码侵入性和业务间的耦合性
const methodExtends = () => {const MethodDecorator = (target, propertyKey, descriptor) => {// 获取方法本身const method = descriptor.value;// 对该方法的生命周期进行操作descriptor.value = (...args) => {console.log('MethodDecorator before run');const data = method.call(this, args);console.log('MethodDecorator after run');return data;};};class Test {method() {return ';;;;';}}__decorate([MethodDecorator], Test.prototype, "method", null);console.log(new Test().method());
};
methodExtends();
// 通过装饰入参的形式对方法进行扩展,使用参数可以对业务进行更强的定制化处理
const methodExtendsByParams = () => {const MethodDecorator = (param) => {console.log('methodExtendsByParams', param);return (target, propertyKey, descriptor) => {// 获取方法本身const method = descriptor.value;// 对该方法的生命周期进行操作descriptor.value = (...args) => {console.log('before run');const data = method.call(this, args);console.log('after run');return data;};};};class Test {method() {return ';;;;';}}__decorate([MethodDecorator('user')], Test.prototype, "method", null);console.log(new Test().method());
};
methodExtendsByParams();

方法参数装饰器

typescript

// 通过装饰器对方法中的属性进行扩展
/*** target 实例自身* methodName 方法名* paramIndex 参数的下标位置*/
const methodParamExtends = () => {const methodPramDecorator = (param: string) => {return (target: any, methodName: string, paramIndex: any) => {console.log('target', target, methodName, paramIndex);target.decoratorData = '222';}};class Test {decoratorData!: string;init (@methodPramDecorator('agent') type: string) {return type;}}const test = new Test();const data = test.init('20230611');console.log(data, test.decoratorData);return data;
};
methodParamExtends();

javascript

var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __param = (this && this.__param) || function (paramIndex, decorator) {return function (target, key) { decorator(target, key, paramIndex); }
};
// 通过装饰器对方法中的属性进行扩展
/*** target 实例自身* methodName 方法名* paramIndex 参数的下标位置*/
const methodParamExtends = () => {const methodPramDecorator = (param) => {return (target, methodName, paramIndex) => {console.log('target', target, methodName, paramIndex);target.decoratorData = '222';};};class Test {init(type) {return type;}}__decorate([__param(0, methodPramDecorator('agent'))], Test.prototype, "init", null);const test = new Test();const data = test.init('20230611');console.log(data, test.decoratorData);return data;
};
methodParamExtends();

寄存器装饰器

typescript

// 通过装饰器对寄存器进行扩展
/*** target 实例自身* propertyKey 属性key* descriptor 属性描述符*/
const setgetExtends = () => {const SetGetDecorator = (param: string) => {return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {const set = descriptor.set;const get = descriptor.get;descriptor.set = function (value) {console.log('GetDecorator before run', value);if (typeof value === 'string') {value += ` set decrator ${param}`;}set.call(this, value);console.log('GetDecorator after run', value);};descriptor.get = function () {console.log('GetDecorator before run', target);let data = get.call(this);console.log('GetDecorator after run');if (typeof data === 'string') {data += ` get decrator ${param}`;}return data;}}};class Test {#name: string;@SetGetDecorator('custom setget')set name (name: string) {this.#name = name;}get name () {return this.#name;}}const user = new Test();user.name = 'user';console.log('setgetExtends', user.name);
};
setgetExtends();

javascript

var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, privateMap, value) {if (!privateMap.has(receiver)) {throw new TypeError("attempted to set private field on non-instance");}privateMap.set(receiver, value);return value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, privateMap) {if (!privateMap.has(receiver)) {throw new TypeError("attempted to get private field on non-instance");}return privateMap.get(receiver);
};
// 通过装饰器对寄存器进行扩展
/*** target 实例自身* propertyKey 属性key* descriptor 属性描述符*/
const setgetExtends = () => {var _name;const SetGetDecorator = (param) => {return (target, propertyKey, descriptor) => {const set = descriptor.set;const get = descriptor.get;descriptor.set = function (value) {console.log('GetDecorator before run', value);if (typeof value === 'string') {value += ` set decrator ${param}`;}set.call(this, value);console.log('GetDecorator after run', value);};descriptor.get = function () {console.log('GetDecorator before run', target);let data = get.call(this);console.log('GetDecorator after run');if (typeof data === 'string') {data += ` get decrator ${param}`;}return data;};};};class Test {constructor() {_name.set(this, void 0);}set name(name) {__classPrivateFieldSet(this, _name, name);}get name() {return __classPrivateFieldGet(this, _name);}}_name = new WeakMap();__decorate([SetGetDecorator('custom setget')], Test.prototype, "name", null);const user = new Test();user.name = 'user';console.log('setgetExtends', user.name);
};
setgetExtends();

属性装饰器

typescript

// 通过属性装饰器对属性进行扩展
const paramExtends = () => {const ParamDecorator = (param: string) => {return function (target: any, key: any) {target[key] = param;console.log(`init param ${key} to ${param}`);}}class Test {@ParamDecorator('www.baidu.com')private url!: string;getName () {return this.url;}}const test = new Test();const data = test.getName();console.log('data', data);
};
paramExtends();

javascript

var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;return c > 3 && r && Object.defineProperty(target, key, r), r;
};
// 通过属性装饰器对属性进行扩展
const paramExtends = () => {const ParamDecorator = (param) => {return function (target, key) {target[key] = param;console.log(`init param ${key} to ${param}`);};};class Test {getName() {return this.url;}}__decorate([ParamDecorator('www.baidu.com')], Test.prototype, "url", void 0);const test = new Test();const data = test.getName();console.log('data', data);
};
paramExtends();

装饰器执行顺序

示例代码

typescript

// 不同装饰器的执行顺序const CustomClassDecorator = (constructor: Function) => {console.log('类装饰器');
};
const CustomMethodDecorator = (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {console.log('方法装饰器');
};
const CustomMethodParamDecorator = (target: any, methodName: string, paramIndex: any) => {console.log('方法参数装饰器');
}
const CustomParamDecorator = (target: any, key: any) => {console.log(`参数装饰器`);
}
const CustomSetGetDecorator = (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {console.log('寄存器装饰器');
}@CustomClassDecorator
class Test {@CustomParamDecoratorsex!: string;#name !: string;@CustomSetGetDecoratorset name (name: string) {this.#name = name;}get name () {return this.#name}@CustomMethodDecoratorhandleName (@CustomMethodParamDecorator prefix: string) {return prefix + this.name;}}
const instance = new Test();
const data = instance.handleName('prefix');
console.log(data);

javascript

// 不同装饰器的执行顺序
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __param = (this && this.__param) || function (paramIndex, decorator) {return function (target, key) { decorator(target, key, paramIndex); }
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, privateMap, value) {if (!privateMap.has(receiver)) {throw new TypeError("attempted to set private field on non-instance");}privateMap.set(receiver, value);return value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, privateMap) {if (!privateMap.has(receiver)) {throw new TypeError("attempted to get private field on non-instance");}return privateMap.get(receiver);
};
var _name;
const CustomClassDecorator = (constructor) => {console.log('类装饰器');
};
const CustomMethodDecorator = (target, propertyKey, descriptor) => {console.log('方法装饰器');
};
const CustomMethodParamDecorator = (target, methodName, paramIndex) => {console.log('方法参数装饰器');
};
const CustomParamDecorator = (target, key) => {console.log(`参数装饰器`);
};
const CustomSetGetDecorator = (target, propertyKey, descriptor) => {console.log('寄存器装饰器');
};
let Test = class Test {constructor() {_name.set(this, void 0);}set name(name) {__classPrivateFieldSet(this, _name, name);}get name() {return __classPrivateFieldGet(this, _name);}handleName(prefix) {return prefix + this.name;}
};
_name = new WeakMap();
__decorate([CustomParamDecorator
], Test.prototype, "sex", void 0);
__decorate([CustomSetGetDecorator
], Test.prototype, "name", null);
__decorate([CustomMethodDecorator,__param(0, CustomMethodParamDecorator)
], Test.prototype, "handleName", null);
Test = __decorate([CustomClassDecorator
], Test);
const instance = new Test();
const data = instance.handleName('prefix');
console.log(data);

执行结果

参数装饰器
寄存器装饰器
方法参数装饰器
方法装饰器
类装饰器
prefixundefined

装饰器原理

以下代码是重写过的,方便理解和回顾。由于Reflect.decorate装饰器反射机制还不支持,且相关资料较少,所以在本文中不进行深入研究。

/*** Test = __decorate([ClassDecrator], Test)* decorators 装饰器列表* target 类实例* key 属性名称* desc 变量属性描述符*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {/*** 获取请求参数,咱本示例中,请求参数为2* Test = __decorate([ClassDecrator], Test)*/var c = arguments.length// 初始化r变量var r = null;// 如果请求参数小于三个,在本示例中满足if (c < 3) {// 将类实例赋予r,也就是将Test赋给rr = target;} else {// 如果属性描述符为空if (desc === null) {// 返回指定属性名的描述符desc = Object.getOwnPropertyDescriptor(target, key);r = desc;} else {// 如果存在描述符,则直接赋予rr = desc;}}// 由此可见,在类装饰器下,r为类实例本身,在方法等装饰器下,r为属性描述符var d;// 是否支持es6的Reflect,暂时跳过,文章后面单独会将if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {r = Reflect.decorate(decorators, target, key, desc)}// 如果不支持es6的Reflect,则向下执行else {// 在这里倒叙循环执行每一个装饰器,由此看出ts装饰器的执行顺序for (var i = decorators.length - 1; i >= 0; i--) {d = decorators[i];if (d) {var temp = null;if (c < 3) {temp = d(r);} else {if (c > 3) {temp = d(target, key, r);} else {temp = d(target, key);}}if (temp) {r = temp;}}}}// 如果参数大于3个,则将属性名和属性描述符赋予该实例if (c > 3 && r) {Object.defineProperty(target, key, r);}// 返回该实例实例或属性描述符return r;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/8162.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

E类变换器仿真

1 参数计算&#xff08;待续&#xff09; &#xff08;1&#xff09;确定振荡频率&#xff1a; &#xff08;2&#xff09;计算各器件参数&#xff1b; 2 电路仿真 &#xff08;1&#xff09;电路图 &#xff08;2&#xff09;电路分析 3 结果 &#xff08;1&#xff09;…

微信小程序+SpringBoot接入后台服务,接口数据来自后端

前言 前两天开发了一个微信小程序项目&#xff0c;只不过接口数据是自己设置的假数据。然后我就想将这些假数据替换掉。这些数据来自接口&#xff0c;之前做过前后端分离的项目&#xff0c;我就想能不能直接调用那些后端数据接口。结果是可以的。以下是自己编写的部分方法 步骤…

【IDE 小程序】小程序控制台 不打印 console.log问题

小程序控制台 不打印 console.log问题 全局搜索compress.drop_console&#xff08;一般在config文件中&#xff09;&#xff0c;设置为false&#xff0c;再重新打开小程序即可

【JUC-7】ReentrantLock (可重入锁)基础

ReentrantLock (可重入锁) ReentrantLock实现了Lock接口, 内部通过继承AQS, 实现了一个同步器. 可以通过同步器来创建Condition条件变量, 可以用作容器, 存放不同条件的等待线程. 说明ReentrantLock与AQS的关系 类图: 相对于synchronized, 都支持可重入. 它还具备如下特点: …

对弈人工智能!myCobot 280开源六轴机械臂Connect 4 四子棋对弈

Connect 4 myCobot280 Introduction Hi,guys.今天我们来介绍一下人工智能下棋&#xff0c;我们将用机械臂来作为对手跟你进行下棋。 人工智能下棋的研究可以追溯到20世纪50年代。当时&#xff0c;计算机科学家开始探索如何编写程序&#xff0c;使计算机能够下象棋。其中最著…

Linux 解决root用户被限制连接服务器

Linux 解决root用户被限制连接服务器 1. 问题描述2. 解决问题2.1 方式一&#xff08;忘记root密码的情况&#xff09;2.2 方式二&#xff08;知道root密码的情况&#xff09; 3. 其他 1. 问题描述 使用 root 用户不能链接服务器&#xff0c;密码对&#xff0c;就是连接不上&am…

VBA之正则表达式(42)-- 快速提取名称

实例需求&#xff1a;A列为待处理数据&#xff0c;现需要从中提取商品名、通用名、胰岛素笔相关信息&#xff0c;保存到B列至D列&#xff0c;需要注意如下几点&#xff1a; 胰岛素笔&#xff08;E列&#xff09;数据只存在于每组产品的第一行记录中&#xff0c;例如第2行数据中…

Apikit 自学日记:保存、使用测试用例

API测试用例是SaaS版本企业版才能使用的功能&#xff0c;免费版用户可通过付费升级后使用。 API管理应用中的测试用例管理涉及到两个场景&#xff1a;单接口测试用例管理 和 多接口测试用例批量测试。 一、单接口测试用例管理 功能入口&#xff1a;API管理应用 / 选中某个项目…

vscode copilot长时间没反应

检测问题 首先看一下OUPUT插件信息有什么异常 如果没有异常&#xff0c;但是也没输出 那是请求没有相应的原因 可以在vscode里设置一下代理 参考&#xff1a;https://github.com/orgs/community/discussions/29127

ArcPy学习心得系列(5)遥感数据中值滤波与均值滤波实现方法(不计入NoDATA)

在数据处理与应用的过程中,我们难免会遇到一些低质量的遥感数据,低质量遥感数据一般是由于天气因素导致的,在云量较多时,卫星传感器所采集到的地面信息被云层所遮挡,导致遥感图像成像过程中产生了较多噪声,对遥感数据的精确度造成了一定影响,如果不解决这些数据中的噪声…

【UE5 Cesium】05-Cesium for Unreal 在子关卡中添加Actor

上一篇&#xff1a;【UE5 Cesium】04-Cesium for Unreal 将不同地区的倾斜摄影作为不同子关卡 步骤 首先将关卡切换到“DenverRooftop” 添加一个“立方体” 将关卡切换到“Globe” 然后再向场景中添加一个“椎体” 此时如果我们将关卡切换到“Boston”&#xff0c;只能看到“…

我的毕业故事

前言 2023年6月25日&#xff0c;在拿到双证(毕业证书/学士学位证书)之后&#xff0c;我正式毕业&#xff0c;结束了本科学习阶段。 由于毕业设计中了盲审&#xff0c;因此在大致顺利的过程中平添了不少故事&#xff0c;这里就对近两个月做一个回顾。 盲审 首先解释一下什么…