美文网首页
ts中方法装饰器拦截方法修改参数,直接修改原型和修改描述valu

ts中方法装饰器拦截方法修改参数,直接修改原型和修改描述valu

作者: 黎明的叶子 | 来源:发表于2021-11-17 10:45 被阅读0次

需求:有个sum函数,传入(1,2,3),输出1+2+3的值为6 。如果传入(1,‘2’,‘3’),输出的为字符串123。希望通过装饰器完成不管输入哪一种,都可以得出相加的和。也就是这两种入参都能得到的是6。

代码如下:

function toNumber(target: any, prop: string, desc: PropertyDescriptor) {
    // console.log(target[prop] === desc.value) // true 
    let oldMethod = desc.value // 指向sum 方法
    //这种写法是可以的
    // desc.value = function (...args: any[]) {
    //     args = args.map(item => parseFloat(item))
    //     return oldMethod.apply(this, args)
    // }
    // 这种写法是不可以的。 但是target[prop] === desc.value 是true的。也就是指向同一个地址的。
    // 分析:这里写成如下这样却不可以。要是想改原始值 只能改desc.value中的。 理论上是可以的 因为target[prop] 表示的就是Person.prototype.sum。猜测是拦截修改的原因。 
    target[prop] = function (...args: any[]) {
        args = args.map(item => parseFloat(item))
        return oldMethod.apply(this, args)
    }
}
class Person {
    @toNumber
    sum(...args: any[]) {
        return args.reduce((accu, item) => {
            return accu + item
        }, 0)
    }
}

let p = new Person()
console.log(p.sum(1, 2, 3))
console.log(p.sum('1', 2, '3'))

如果直接顺序修改原型的方法,绝对是可以修改的了的。所以看一下拦截是如果做到的,对其做了些什么。猜测拦截用到了Object.defineProperty。
可以把当前ts转成js,然后分析是如果做到的。转成的js如下:

"use strict";
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;
};
function toNumber(target, prop, desc) {
    // console.log(target[prop] === desc.value) // true 
    var oldMethod = desc.value; // 指向sum 方法
    //这种写法是可以的
    // desc.value = function (...args: any[]) {
    //     args = args.map(item => parseFloat(item))
    //     return oldMethod.apply(this, args)
    // }
    // 这种写法是不可以的。 但是target[prop] === desc.value 是true的。也就是指向同一个地址的。
    // 分析:这里写成如下这样却不可以。要是想改原始值 只能改desc.value中的。 理论上是可以的 因为target[prop] 表示的就是Person.prototype.sum。猜测是拦截修改的原因。 
    target[prop] = function () {
        var args = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            args[_i] = arguments[_i];
        }
        args = args.map(function (item) { return parseFloat(item); });
        return oldMethod.apply(this, args);
    };
}
var Person = /** @class */ (function () {
    function Person() {
    }
    Person.prototype.sum = function () {
        var args = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            args[_i] = arguments[_i];
        }
        return args.reduce(function (accu, item) {
            return accu + item;
        }, 0);
    };
    __decorate([
        toNumber
    ], Person.prototype, "sum", null);
    return Person;
}());
var p = new Person();
console.log(p.sum(1, 2, 3));
console.log(p.sum('1', 2, '3'));

源码乍一看,内容比较多,这里分享一下心得。第一需要耐心,一点点缕顺。第二看的过程把不必要的删掉。还有一些判断都可以删除掉。最后得到精简的代码。
上面代码可以分解为如下代码:

//[toNumber ], Person.prototype, "sum", null
function __decorate(decorators, target, key, desc) {
    var c = 4
    var r = desc = Object.getOwnPropertyDescriptor(target, key)
    var d; 
    for (var i = decorators.length - 1; i >= 0; i--) {
        d = decorators[i]
        r =  d(target, key, r) || r;
    } 
    Object.defineProperty(target, key, r)
    return r; 
};
function toNumber(target, prop, desc) {
    // console.log(target[prop] === desc.value) // true 
    var oldMethod = desc.value; // 指向sum 方法
    //这种写法是可以的
    // desc.value = function (...args: any[]) {
    //     args = args.map(item => parseFloat(item))
    //     return oldMethod.apply(this, args)
    // }
    // 这种写法是不可以的。 但是target[prop] === desc.value 是true的。也就是指向同一个地址的。
    // 分析:这里写成如下这样却不可以。要是想改原始值 只能改desc.value中的。 理论上是可以的 因为target[prop] 表示的就是Person.prototype.sum。猜测是拦截修改的原因。 
    target[prop] = function () {
        var args = [];
        for (var _i = 0; _i < arguments.length; _i++) {
            args[_i] = arguments[_i];
        }
        args = args.map(function (item) { return parseFloat(item); });
        return oldMethod.apply(this, args);
    };
}
function Person() {
}
Person.prototype.sum = function () {
    var args = [];
    for (var _i = 0; _i < arguments.length; _i++) {
        args[_i] = arguments[_i];
    }
    return args.reduce(function (accu, item) {
        return accu + item;
    }, 0);
};
__decorate([
    toNumber
], Person.prototype, "sum", null);

var p = new Person();

console.log(p.sum(1, 2, 3));
console.log(p.sum('1', 2, '3'));

主要是__decorate方法实现。就是装饰器的模拟实现。在调用之后,把参数传过来,然后代码里面,可以通过具体分析的参数来代进入。比如c得到的就是4.这样一步步来。

//[toNumber ], Person.prototype, "sum", null
function __decorate(decorators, target, key, desc) {
    var c = 4 // 获取参数个数,源码中通过这个来判断很多要执行哪个。这个把它具体化,然后把那些判断都也删除掉。
   // 这里获取此属性的描述信息
    var r = desc = Object.getOwnPropertyDescriptor(target, key)
    var d; 
   // 装饰器可以传多个,所以入参用数组。并且这里相当于一个链式调用,挨着近的先执行,并把执行结果传给下一个装饰器。
    for (var i = decorators.length - 1; i >= 0; i--) {
        d = decorators[i]
        r =  d(target, key, r) || r;
    } 
    Object.defineProperty(target, key, r) // 这里是关键 通过Object.defineProperty来改变的。所以desc.value 可以,但是改原型不行。如果这行删除了。改原型就可以了,desc.value就不可以了。 所以这里是关键。
    return r; 
};

以上为提出问题,分析问题的思路。有时候这种疑问 ,还真得看如何实现才能理解。因为疑惑了半天。才找到思路。
记录一下~~~每天一点小进步!

相关文章

  • golang设计模式

    方法装饰器 可以扩展为修改对象的方法,并做拦截

  • vue+element-ui+axios上传excel文件

    1.不要使用自己封装的方法,拦截器会修改参数,直接使用最纯净的axios

  • 类里方法的装饰器

    类的方法的装饰器 方法的装饰器的执行时间也是在类定义之后,立即对类的方法进行装饰修改 方法的装饰器接受3个参数 ...

  • Swift如何修改方法参数

    在Swift中不允许修改方法函数中的参数值,因为方法中参数传递的是值而不是地址,参数被修改时编译器会认为此参数为常...

  • matplotlib图例中文乱码解决方法

    原代码 原图 修改方法1 直接在代码后进行参数设置 修改后的图 修改方法2 然后进入C:\\Users\\sw1\...

  • @装饰器的学习,类装饰器

    //装饰器的学习 // 通过装饰器可以拿到目标类,同时为他增加一些额外的属性或者方法 //甚至修改内部的描述,...

  • 10.装饰器

    装饰器: 装饰器是一种特殊类型的声明,它可以被附加到类声明, 方法、属性或参数上,可以修改类的行为简单的说 装饰器...

  • Java学习笔记 - 第008天

    每日要点 修改器和访问器 修改器 - 属性的setter方法 访问器 - 属性的getter方法 toString...

  • TypeScript的装饰器

    装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为,通俗来讲装饰器就是一个方法...

  • python的装饰器

    装饰器 不修改函数调用方式,依旧可以修改输出 利用 *args 可以传入任意个参数 装饰器的最终形态,**kw 代...

网友评论

      本文标题:ts中方法装饰器拦截方法修改参数,直接修改原型和修改描述valu

      本文链接:https://www.haomeiwen.com/subject/nzjdtrtx.html