zoukankan      html  css  js  c++  java
  • ES6 类的正确定义方式 公有类字段 getter / setter

    类字段提案

    https://github.com/tc39/proposal-class-fields
    https://wenjun.me/2019/07/public-class-fields.html

    Webpack 内置的js打包程序不支持类字段, 然而Chrome和FF已原生支持....

    getter / setter

    注意与Java区别.

    • setter函数的定义
      setter函数, 对该属性赋值时触发函数调用.
    class A {
        constructor() {
            this.field = 'init from contructor()'; # 这里也会触发setter, 即使这里没有分配field变量, 之后的赋值语句也会将setter函数触发
        }
        set field(value) {
            console.log(`setter被触发, 你想将field设置为${typeof value}: ${value}`);
        }
    }
    
    let a = new A(); // setter被触发, 你想将field设置为string: init from contructor()
    a.field = 'trigger setter()'; // setter被触发, 你想将field设置为string: trigger setter()
    console.log(a.field); // undefined
    
    • getter函数
      与setter类似.
    ...
        get field() {
            console.log('你想获取field的值'); // 被打印
            return '未知';
        }
    ...
    console.log(a.field); // 未知
    
    • 如何工作
      我们想当然地以为下面的代码可以工作, 事实却很残酷:
    set field(value) {
        console.log(`setter被触发, 你想将field设置为${typeof value}: ${value}`); # 不断打印该行, 直到报错
        this.field = value; # 会导致递归地调用该函数
    }
    

    如何解决? 使用私有的公有类字段即可:
    getter 与 setter 在访问公有类字段的时候不会被触发, 在公有类中可以定义一个私有的公有类字段来缓存.

    class A {
        #field = undefined; // #开头的公有字段是私有的, 不可以通过实例名直接访问
        constructor() {
        }
        set field(value) {
            console.log(`setter被触发, 你想将field设置为${typeof value}: ${value}`);
            this.#field = value;
        }
        get field() {
            console.log('你想获取field的值');
            return this.#field;
        }
    }
    
    let a = new A();
    a.field = 'trigger setter()';
    console.log(a.field);
    
    
    setter被触发, 你想将field设置为string: trigger setter()
    你想获取field的值
    trigger setter()
    

    这里使用了#声明私有的公有类字段, 保证字段不会被外界获取, 即使使用以下方式:

    function tryGetPrivateField() {
        return this.#field;
    }
    
    console.log(tryGetPrivateField.apply(a)); # 语法错误:私有字段“#field”必须在封闭类中声明
    

    实例字段与静态字段

    • 静态字段
      类也是一个对象, 可以为类赋值, 通过类名获取变量值, 称为静态字段:
    class A {}
    A.field = 'value';
    
    // 静态字段的便捷写法: 使用static关键字以及公有字段的声明方式
    class A {
          static field = 'value';
    }
    
    • 实例字段
      实例字段比较常见, 通常在构造函数中动态赋值. 当然, 我们建议使用公有字段的声明方式.
      赋值和公有字段的区别: 前者使用"分配", 后者使用"定义". 即普通赋值和Object.defineProperty()函数. 这两者又有什么区别?

    共有类字段

    JavaScript的类只有函数是可复用的, 一般来说没有什么问题:
    如果字段需要共享, 那么可能直接往类的原型CustomClass.prototype上添加属性即可, 原型委托机制能让类的实例访问这些共享字段. (使用静态字段也何尝不可)

    类特有的字段只能在构造函数中动态赋值, 但是这对于IDE来说不友好, 因为我们希望一些字段是通用的, 公有的, 可提示的.

    ES6有一个提案: 公有类字段, 可以提供实现:

    class CustomClass {
          version = '1.0'; // 声明公有类字段
          /** 创建该类的作者 */ // 为字段编写文档
          author; // 可以声明但不赋值
    
          constructor(version, author = 'Develon') { // 如果需要在构造函数中提供赋值的话, 可以使用默认参数, 而不在声明author时赋值
                // this.version = version; // 不建议单独在构造函数中使用this关键字构造字段, 而省略公有类字段的声明
                if (version !== undefined) this.version = version; // 公有类字段的正确构造方法, 毕竟构造函数不可重载(难不成基于参数个数重载? 可变参数咋办呢....)
                this.author = author;
          }
    
          printVersion() {
                console.log(this.version);
          }
    }
    
    new CustomClass().printVersion(); // 1.0
    console.log(new CustomClass()); // CustomClass { version: '1.0', author: 'Deve'on' }
    

    共有类字段也可以是函数, 一般来说是箭头函数, 可以捕获this关键字, 这在React中有应用:

    class CustomComponent extends Component {
          constructor(props) {
                super(props);
          }
          handleClick = event => { // 要紧: 公有类字段 + 箭头函数
                this === inst of CustomComponent // 捕获到this关键字, 将函数引用注册到按钮事件也不会丢失this值
                use event && this.setState(...); // 正确执行, 根据事件设置组件状态
          };
          render() {
                return createElement('button', {
                      onClick: this.handleClick,
                      onClick: event => { this.handleClick(event); }, // 也可以直接在这里使用this关键字, 但是需要显式传递事件
                });
          }
    }
    

    私有类字段

    使用#符号即可.

  • 相关阅读:
    linux 文件记录锁详解
    Linux fcntl函数详解
    大数相加
    信雅达面试题atoi函数实现
    linux getopt函数详解
    strcpy和memcpy的区别
    手把手写数据结构之栈操作
    手把手写数据结构之队列操作
    手把手写数据结构之双向链表操作
    ORACLE查询内存溢出
  • 原文地址:https://www.cnblogs.com/develon/p/13614113.html
Copyright © 2011-2022 走看看