粥里有勺糖

vuePress-theme-reco 粥里有勺糖    2018 - 2023
粥里有勺糖 粥里有勺糖

Choose mode

  • dark
  • auto
  • light
关于我
备战春秋
  • 心得总结
  • 校招考点汇总
  • 面经汇总
  • 复习自查
技术笔记
  • 技术教程
  • 模板工程
  • 源码学习
  • 技术概念
  • 个人作品
  • 学习笔记
计算机基础
  • 算法与数据结构
  • 操作系统
  • 计算机网络
  • 设计模式
  • 剑指offer
大前端
  • javascript
  • vue
  • html
  • css
  • 🌏浏览器专题
  • Web性能优化
  • regexp
  • node
面试
  • 问解
  • javascript
  • css
  • 手撕代码
  • 性能优化
  • 综合问题
  • 面经汇总
  • 小程序
手撕代码
  • 数据结构与算法
  • javascript
  • css
个人站点
  • GitHub (opens new window)
  • 博客园 (opens new window)
  • 掘金 (opens new window)
线上作品
  • 轻取(文件收集) (opens new window)
  • 个人图床 (opens new window)
  • 考勤小程序 (opens new window)
  • 时光恋人 (opens new window)
  • 在线简历生成 (opens new window)
留言板
Github (opens new window)
author-avatar

粥里有勺糖

285

文章

40

标签

关于我
备战春秋
  • 心得总结
  • 校招考点汇总
  • 面经汇总
  • 复习自查
技术笔记
  • 技术教程
  • 模板工程
  • 源码学习
  • 技术概念
  • 个人作品
  • 学习笔记
计算机基础
  • 算法与数据结构
  • 操作系统
  • 计算机网络
  • 设计模式
  • 剑指offer
大前端
  • javascript
  • vue
  • html
  • css
  • 🌏浏览器专题
  • Web性能优化
  • regexp
  • node
面试
  • 问解
  • javascript
  • css
  • 手撕代码
  • 性能优化
  • 综合问题
  • 面经汇总
  • 小程序
手撕代码
  • 数据结构与算法
  • javascript
  • css
个人站点
  • GitHub (opens new window)
  • 博客园 (opens new window)
  • 掘金 (opens new window)
线上作品
  • 轻取(文件收集) (opens new window)
  • 个人图床 (opens new window)
  • 考勤小程序 (opens new window)
  • 时光恋人 (opens new window)
  • 在线简历生成 (opens new window)
留言板
Github (opens new window)
  • source learn

    • 源码学习
    • 优秀装饰器源码学习(一):time
    • 优秀装饰器源码学习(二)
    • 优秀装饰器源码学习(三)
    • FileSaver.js源码学习,纯前端实现文件下载,防止浏览器直接打开预览
    • 源码学习:MongoDB-ObjectId生成原理
    • 源码学习:Vite中加载环境变量(loadEnv)的实现

优秀装饰器源码学习(二)

vuePress-theme-reco 粥里有勺糖    2018 - 2023

优秀装饰器源码学习(二)

粥里有勺糖 2021-06-27 技术笔记源码学习

# 优秀装饰器源码学习(二)

# 前言

上一篇文章:优秀装饰器源码学习(一):time

本篇先学习一些初级较简单的 @deprecate, @readonly, @enumerable, @nonconfigurable

# deprecate

用于提示XX API/方法已经被弃用

# 使用示例

使用如下,通过一个简单的 @deprecate 即可在函数执行的时候抛出 API已被弃用的警告

其中 deprecate与deprecated效果一致,只是不同的别名

export { default as deprecate, default as deprecated } from './core/deprecate'
1
import { deprecate, deprecated } from '../index'
class Test {
    @deprecate()
    sayHello1() {
        console.log('hello 1');
    }
    @deprecated('API弃用警告')
    sayHello2() {
        console.log('hello 2');
    }
    @deprecate('API弃用警告',{url:'https://www.baidu.com'})
    sayHello3() {
        console.log('hello 3');
    }
}

const t = new Test()
t.sayHello1()
t.sayHello2()
t.sayHello3()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 执行效果

图片

# 函数结构

传入参数:

  • msg:有默认内容
  • options:通过url属性进一步指定文档链接
const DEFAULT_MSG = 'This function will be removed in future versions.';

interface Options{
    url?:string
}

export default function deprecate(msg = DEFAULT_MSG, options:Options = {}) {
    return function (target, key, descriptor) {

    }
}
1
2
3
4
5
6
7
8
9
10
11

# 最终实现

const DEFAULT_MSG = 'This function will be removed in future versions.';

interface Options{
    url?:string
}

export default function deprecate(msg = DEFAULT_MSG, options:Options = {}) {
    return function (target, key, descriptor) {
        // 如果被装饰对象不是函数,直接抛出错误
        if (typeof descriptor.value !== 'function') {
            throw new SyntaxError('Only functions can be marked as deprecated');
        }

        // 生成方法的签名(反应来自与xx类xx方法)
        const methodSignature = `${target.constructor.name}#${key}`;

        // 如果有线上地址的文档描述原因,则展示一下这个地址
        if (options.url) {
            msg += `\n\n    See ${options.url} for more details.\n\n`;
        }

        return {
            ...descriptor,
            value: function deprecationWrapper() {
                // 打印警告信息
                console.warn(`DEPRECATION ${methodSignature}: ${msg}`);
                // 执行函数
                return descriptor.value.apply(this, arguments);
            }
        };
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# readonly

将指定属性变为只读,即不可在实例化后更改属性的内容

# 使用示例

使用如下,通过一个简单的 @readonly 即可将目标属性变为只读

import { readonly } from '../index';
class Test {
    hello1(){
        console.log('hello1');
    }

    @readonly
    hello2(){
        console.log('hello2');
    }
}

const t = new Test();
t.hello1 = function(){
    console.log('1');
}

t.hello1()

t.hello2 = function(){
    console.log('2');
}

t.hello2()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 执行效果

图片

# 函数实现

无需额外传参,直接通过修改装饰对象的descriptor上的writable属性为false实现

export default function readonly(target, key, descriptor) {
    descriptor.writable = false
    return descriptor
}
1
2
3
4

# enumerable、nonenumerable、enumable

更改装饰对象的enumerable属性值

# 使用示例

import enumable from "../core/enumable";
import enumerable from "../core/enumerable";
import nonenumerable from "../core/nonenumerable";

class Test {
    @nonenumerable
    a(){
        
    }
    @enumerable
    b(){

    }
    @enumable(false)
    c(){

    }
}

console.log(Object.getOwnPropertyDescriptor(Test.prototype,'a')?.enumerable === false); // true
console.log(Object.getOwnPropertyDescriptor(Test.prototype,'b')?.enumerable === true);  // true
console.log(Object.getOwnPropertyDescriptor(Test.prototype,'c')?.enumerable === false); // true

console.log(Object.keys(Test.prototype)); // ['b']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 实现

这个比较简单就是修改一下装饰对象的enumerable值

# enumerable

export default function enumerable(target, key, descriptor) {
    descriptor.enumerable = true
    return descriptor
}
1
2
3
4

# nonenumerable

export default function nonenumerable(target, key, descriptor) {
    descriptor.enumerable = false
    return descriptor
}
1
2
3
4

# enumable

export default function enumable(v = true) {
    return function (target, key, descriptor) {
        descriptor.enumerable = v
        return descriptor
    }
}
1
2
3
4
5
6

# nonconfigurable

设置装饰对象的configurable属性为false

当且仅当 configurable 为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。

# 使用示例

import { nonconfigurable } from "../index";

class Test {
    @nonconfigurable
    a(){
        
    }
    b(){

    }
}

let prototype:any = Test.prototype
delete prototype.b
console.log(Object.keys(Test.prototype)); // ['a']
delete prototype.a // 抛出错误: Cannot delete property 'a' of #<Test>
console.log(Object.keys(Test.prototype));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 实现

这个依旧比较简单就是修改一下装饰对象的configurable值

export default function nonconfigurable(target, key, descriptor) {
    descriptor.configurable = false
    return descriptor
}
1
2
3
4

# 未完待续

下一篇将学习:

  • @mixin:混入方法到类中
  • @lazyInitialize:在使用的时候才初始化目标属性
  • @debounce:防抖
  • @throttle:节流
Edit this page (opens new window)
Last Updated: 2022/5/15 12:46:34