当前位置:首页>python>装饰器(Python & TypeScript)一款优雅的语法糖

装饰器(Python & TypeScript)一款优雅的语法糖

  • 2026-10-21 13:28:52
装饰器(Python & TypeScript)一款优雅的语法糖
一、什么是语法糖🍯?
语法糖概念最早是在上个世纪六十年代由Peter J. Landin提出。
两处原文地址如下:

原文地址

https://www.cs.cmu.edu/~crary/819-f09/Landin66.pdf

原文地址

https://dl.acm.org/doi/epdf/10.1145/365230.365257
用开发者角度,简短概括就是:“编程语言中不改变核心功能、仅简化代码书写、提升可读性的便捷语法形式”
二、什么是装饰器🎊?
装饰器是语法糖的一种,做到了使用简单和非强耦合。这在代码后续维护和复用上起到了至关重要的作用。如此巧妙的设计接下来我将用Python和TS分别展示这个优雅的绅士🤵
2.1、Python装饰器
让我们看一组示例代码来逐步说明装饰器的作用
上面代码很简单就是统计1到100加和的耗时。
但是上面代码存在两个关键问题:
  • 不同业务逻辑混淆,第5、7行统计耗时和第6行计算累加业务,不应该放一起。
  • 破坏原函数侵入式修改,不应该将统计耗时写在incr业务函数内。
既如此我们修改下上面代码
就是将统计耗时逻辑,放在了业务函数incr调用(第8行)的前后(第7、9行),
这样就解决了上面的两个问题,不同业务逻辑混淆和破坏原函数侵入式修改(实际这是大部分开发都会遇到问题)。但是似乎又有了新问题,就是那两行统计耗时逻辑,完全没有复用性可言。那我们继续修改。
这样写就又解决了复用性问题16到18行正是复用性体现。
难道这就行了吗?
当我们看到第15行和第19行时候
f2 = decorator(incr)f2(101)
不一定立即就能看得出来,是调用了incr函数并传入了101作为实参。
incr(101)
还是不够直观,我们还要继续修改。
这次就非常直观了,第12行定义incr函数,第16行调用incr函数。而且这几个版本的改动完全没有影响统计耗时(每次修改都有重新执行,下面的时间每次都是不同的)。
这个就非常神奇,我们只关注到了主业务逻辑incr函数,但是统计耗时逻辑decorator函数也正常执行了,可我们并没有感觉到他的存在。如此就简化了主函数复杂度和调用流程。
那么大家应该观察到了原代码的第11行有点不同,多了一个 @decorator
@decoratordef incr(total):    pass;
这个就是Python装饰器用法,用起来很简单设计得也很巧妙。
只需在原函数上面一行挨着写一个@符号并加上装饰器名字即可使用。
就能够实现:不混淆主业务逻辑 和 不侵入原代码进行功能补充
上面是Python的函数装饰器,还有类装饰器以及装饰器的传参这里就不在演示了。
需要注意的是装饰器在函数被定义的时候就开始调用了。
上图中incr函数仅仅被定义并没有被调用但装饰器依然执行了。
2.2、TypeScript装饰器(非JavaScript原生实现)
实际截止到目前2026年1月份,JavaScript装饰器始终处于tc39提案的S3阶段(tc39-Stage: 3)还没有正式通过。

ES6类装饰器GitHub文档地址

https://github.com/tc39/proposal-decorators
至于什么时候才能被各大浏览器厂商完全接入,那就更是遥遥无期了。
从“ES6类装饰器” GitHub仓库创建时间来看已经有8年了,似乎纷争还没有结束。
这个是README最后面一段话
我截取一段GitHub上关于JavaScript装饰器说明(汉化后的)

纵使原生 JavaScript 的装饰器尚未被正式纳入 ECMAScript 标准,但其超集 TypeScript 已率先实现了装饰器语法(TS等不了先手搓一个)。

TypeScript实现不同于Python有函数装饰器和类装饰器,他根据ECMAScript装饰器提案也就是上面提到的tc39提案,只实现了类及成员装饰器,并没有单独的函数装饰器。

TypeScript装饰器语法

https://docs.lyfdev.com/typescript/decorator
让我们看一个简单的ts类装饰器示例代码
那么上面这段ts转js后是什么样的呢?
我们打开下面这个ts转js地址,这个网站是由微软官方维护的。

ts转js在线地址

https://www.typescriptlang.org/play/
转换后js如下所示
"use strict";var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {    function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }    var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";    var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;    var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});    var _, done = false;    for (var i = decorators.length - 1; i >= 0; i--) {        var context = {};        for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];        for (var p in contextIn.access) context.access[p] = contextIn.access[p];        context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };        var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);        if (kind === "accessor") {            if (result === void 0) continue;            if (result === null || typeof result !== "object") throw new TypeError("Object expected");            if (_ = accept(result.get)) descriptor.get = _;            if (_ = accept(result.set)) descriptor.set = _;            if (_ = accept(result.init)) initializers.unshift(_);        }        else if (_ = accept(result)) {            if (kind === "field") initializers.unshift(_);            else descriptor[key] = _;        }    }    if (target) Object.defineProperty(target, contextIn.name, descriptor);    done = true;};var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {    var useValue = arguments.length > 2;    for (var i = 0; i < initializers.length; i++) {        value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);    }    return useValue ? value : void 0;};var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {    if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";    return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });};function simpleDecorator(value, context) {    console.log(`hi, this is ${context.kind}${context.name}`);    return value;}let A = (() => {    let _classDecorators = [simpleDecorator];    let _classDescriptor;    let _classExtraInitializers = [];    let _classThis;    var A = _classThis = class {    };    __setFunctionName(_classThis, "A");    (() => {        const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;        __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);        A = _classThis = _classDescriptor.value;        if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });        __runInitializers(_classThis, _classExtraInitializers);    })();    return A = _classThis;})(); // "hi, this is class A"n
输出结果也和我们预想的相同 
hi, this is class A
不难看出,如果没有ts包装纯js手搓装饰器,代码量和难度可以劝退一大部分人。
TypeScript类装饰器其他用法就不在演示了,有兴趣的同学可以访问下面地址去学习。

TypeScript装饰器语法

https://docs.lyfdev.com/typescript/decorator

最新文章

随机文章