【3.8.3】自定装饰器的get/set不生效的问题

先上代码

import { _decorator, Component, Node } from 'cc';
const { ccclass, property } = _decorator;

export function Column(): any {
    return function (target: any, key: string, desc: PropertyDescriptor) {
        console.info(`[装饰器] target[${Object.keys(target)}] key[${key}]`);

        desc.configurable = false
        desc.enumerable = true
        desc.get = function () { throw new Error('爆1') }
        desc.set = function () { throw new Error('爆2') }
        return desc
    }
}

export class A {
    @Column()
    age?: number = 0
}

@ccclass('test')
export class test extends Component {
    protected onLoad(): void {
        let a = new A();
        console.info(`a[${a}] a.age[${a.age}]`)
        a.age = 25;
        console.info(`a[${a}] a.age[${a.age}]`)
    }
}


然后是执行画面


getset 方法都没有被设置到

之前用 2.4.x 版的时候是能正常运作的

是还有哪需要设定吗?

请各位前辈指教一下,感谢

经过寻找,我知道怎么解决了

首先在项目的 temp/programming/packer-driver/targets 目录下可以找到编译成js的档案

上面写的class A会被编译为


			({
				ccclass,
				property
			} = _decorator);

			_export("A", A = (_dec = Column(), (_class = class A {
				constructor() {
					_initializerDefineProperty(this, "age", _descriptor, this);
				}

			}, (_descriptor = _applyDecoratedDescriptor(_class.prototype, "age", [_dec], {
				configurable: true,
				enumerable: true,
				writable: true,
				initializer: null
			})), _class)));

然后在这个 _applyDecoratedDescriptor 方法里执行

	function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
		var desc = {};
		Object.keys(descriptor).forEach(function (key) {
			desc[key] = descriptor[key];
		});
		desc.enumerable = !!desc.enumerable;
		desc.configurable = !!desc.configurable;
		if ('value' in desc || desc.initializer) {
			desc.writable = true;
		}
		desc = decorators.slice().reverse().reduce(function (desc, decorator) {
			return decorator(target, property, desc) || desc;
		}, desc);
		if (context && desc.initializer !== void 0) {
			desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
			desc.initializer = undefined;
		}
		if (desc.initializer === void 0) {
			Object.defineProperty(target, property, desc);
			desc = null;
		}
		return desc;
	}

这边在执行decorates的时候会带入一个赋值过的desc对象

desc = decorators.slice().reverse().reduce(function (desc, decorator) {
	return decorator(target, property, desc) || desc;
}, desc);

单看产生出来的代码觉得似乎没什么问题,
但就是不能正常工作
只好认为是该desc在传入的时候已经做过某些设定

故最终把代码改成自已建立一个desc回传就可以了

1赞

虽然不懂为什么要这样设计,但是有解决就好…

希望能给未来遇到同样问题的人解答

1赞

我个人习惯装饰器工厂函数第三个参数写成可选形式 告诉自己有时候是空的 :joy: 好像属性都是空的 寄存器才是有的 然后configurable为true enumerable为false

1赞

必须Mark,太给力了,我之前怎么找也没找到问题所在,get set就是不管用,用你这办法好用,赞赞赞

2赞

装饰器(旧语法)
这个文档说属性装饰器的返回值会被忽略,你这个咋没有忽略?

对这个示例的理解应该是 装饰器工厂函数接收自定义参数然后返回一个真正的装饰器函数
在类实例成员装饰器中 Object.defineProperty的第三个参数是descriptor 正是本来应该返回的属性描述符
既然已经修改了那自然不需要再返回一个具体的值了

这和类装饰器是一样的 类装饰器通常情况下就是不需要返回具体的值的 当然你也可以返回一个new Class(几乎不会这样做)

1赞

这个最基本的写法有试过,的确是不生效,能生效就不会发帖了

借楼问一个装饰器问题

class Base { get aaa() { return } }
class Test extends Base {
    @test
    override get aaa() { return }
}
function test(target, property) {
    console.log('@', property);
}

当装饰器附加在用override修饰的重写父类getter/setter的属性上时 奇怪的事情发生了…
这时候打印结果显示属性名居然不是aaa 变成了override :joy: 构建发布后也是一样
排查的时候我都惊呆了 这是什么奇葩特性 只不过是用了override修饰符而已…真是颠覆我认知

有知道是咋回事的吗 :joy:

去看了下编译结果 是装饰器函数编译出了问题 :joy: 怪事真多

__decorate([
    test
], Test.prototype, "override", void 0); // 正常这里是"aaa"

没怎么用过override关键字,
理论上你的情况不加override应该也是可以吧

:joy:就是标记一下告诉自己重写了父类的属性或方法 修饰性质的关键字确实没啥用 可能写业务代码public这些也都不写了 上面主要是反馈编译的问题 可能是我用的2.x太落后了?

理解,
因2.x 跟 3.x 的编译系统是不同的
我一开始的代码在 2.4.x 还是能正常跑的

以前是用gulp先把库编译成es5代码再放进编辑器的node_modules
这种方法估计也能绕过这些问题
因为类在目前的js里就是 function的 prototype 指来指去而已

现在是用上了esmodule觉得简洁

不过若是把装饰器应用在asserts里再让creator编译的话
真的就会遇上这篇的问题了

我也遇到很奇怪的问题,我在初始化赋值的时候不会调用setter
比如
@desc
public testA:number = 100;

这句话不会触发setter, 但是2.x会触发