• ES6 Class(类) 总结(九)


    ES6 中的 class 是一种面向对象编程的语法糖,提供了一种简洁的方式来定义对象的结构和行为。

    JavaScript 语言中,生成实例对象的传统方法是通过构造函数。下面是一个例子。

    function Point(x, y) {
      this.x = x;
      this.y = y;
    }
    Point.prototype.toString = function () {
      return '(' + this.x + ', ' + this.y + ')';
    };
    var p = new Point(1, 2);
    

    基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的 class 改写,就是下面这样:

    class Point {
      constructor(x, y) {
        this.x = x;
        this.y = y;
      }
      toString() {
        return '(' + this.x + ', ' + this.y + ')';
      }
    }
    

    ES6 的类,完全可以看作构造函数的另一种写法:

    class Point {
      // ...
    }
    typeof Point // "function"
    Point === Point.prototype.constructor // true
    
    ----------------------------------------------------------------------------
    class Point {
      constructor() {
        // ...
      }
      toString() {
        // ...
      }
      toValue() {
        // ...
      }
    }
    // 等同于
    Point.prototype = {
      constructor() {},
      toString() {},
      toValue() {},
    };
    

    主要特性:

    1. 声明式语法:使用 class 关键字声明类。
    2. 构造函数:使用 constructor 方法初始化类实例。
    3. 实例方法:定义在类内部的普通方法,使用 this 访问实例属性。
    4. 静态方法:使用 static 关键字定义,不依赖于类的实例。
    5. 实例属性:在构造函数中初始化,或使用字段声明语法(目前是 Stage 3 proposal)。
    6. 继承:使用 extends 关键字实现。
    7. super 关键字:在子类的构造函数中调用父类的构造函数或方法。
    8. getter 和 setter:使用 get 和 set定义属性的访问器。
    9. 私有属性和方法:使用 # 定义私有属性和方法(目前是 Stage 3 proposal)。

    1. 基本类定义和实例化

    class Point {
        constructor(x, y) {
            this.x = x;
            this.y = y;
        }
    
        toString() {
            return `Point(${this.x}, ${this.y})`;
        }
    }
    
    let point = new Point(10, 20);
    console.log(point.toString()); // 输出: Point(10, 20)
    

    2. 静态方法、属性

    class MathUtils {
    	constructor() {
        	console.log(MyClass.myStaticProp); // 42
        }
        static add(a, b) {
            return a + b;
        }
        static myStaticProp = 42;
    }
    
    console.log(MathUtils.add(1, 2)); // 输出: 3
    

    3. 继承和 super

    class Rectangle {
        constructor(width, height) {
            this.width = width;
            this.height = height;
        }
    
        area() {
            return this.width * this.height;
        }
    }
    
    class Square extends Rectangle {
        constructor(sideLength) {
            super(sideLength, sideLength);
        }
    }
    
    let square = new Square(5);
    console.log(square.area()); // 输出: 25
    

    4. getter 和 setter

    class Rectangle {
        constructor(width, height) {
            this.width = width;
            this.height = height;
        }
    
        get area() {
            return this.width * this.height;
        }
    
        set width(newWidth) {
            if (newWidth > 0) {
                this.width = newWidth;
            } else {
                console.log("Width must be positive.");
            }
        }
    }
    
    let rect = new Rectangle(4, 5);
    console.log(rect.area); // 输出: 20
    rect.width = -10; // 输出: Width must be positive.
    

    class的注意点

    (1)严格模式

    类和模块的内部,默认就是严格模式,所以不需要使用 use strict 指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。考虑到未来所有的代码,其实都是运行在模块之中,所以 ES6 实际上把整个语言升级到了严格模式。

    (2)不存在提升

    类不存在变量提升(hoist),这一点与 ES5 完全不同。

    new Foo(); // ReferenceError
    class Foo {}
    
    //不会报错
    //因为 Bar 继承 Foo 的时候, Foo 已经有定义了。
    //但是,如果存在 class 的提升,上面代码就会报错,
    //因为 class 会被提升到代码头部,而 let 命令是不提升的,
    //所以导致 Bar 继承 Foo 的时候, Foo 还没有定义。
    {
      let Foo = class {};
      class Bar extends Foo {
      }
    }
    

    (3)name 属性

    由于本质上,ES6 的类只是 ES5 的构造函数的一层包装,所以函数的许多特性都被Class继承,包括 name 属性。

    class Point {}
    Point.name // "Point"
    //name 属性总是返回紧跟在 class 关键字后面的类名。
    

    (4)Generator 方法

    如果某个方法之前加上星号( * ),就表示该方法是一个 Generator 函数。

    class Foo {
      constructor(...args) {
        this.args = args;
      }
      * [Symbol.iterator]() {
        for (let arg of this.args) {
          yield arg;
        }
      }
    }
    for (let x of new Foo('hello', 'world')) {
      console.log(x);
    }
    // hello
    // world
    
    //Foo 类的 Symbol.iterator 方法前有一个星号,表示该方法是一个 Generator 函数。 
    //Symbol.iterator 方法返回一个 Foo 类的默认遍历器, for...of 循环会自动调用这个遍历器。
    

    (5)this 的指向

    类的方法内部如果含有 this ,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错。

    class Logger {
      printName(name = 'there') {
        this.print(`Hello ${name}`);
      }
      print(text) {
        console.log(text);
      }
    }
    const logger = new Logger();
    const { printName } = logger;
    printName(); // TypeError: Cannot read property 'print' of undefined
    

    避免使用this,在构造方法中绑定 this:

    class Logger {
      constructor() {
        this.printName = this.printName.bind(this);
      }
      // ...
    }
    

    避免使用this,使用箭头函数:

    class Obj {
      constructor() {
        this.getThis = () => this;
      }
    }
    const myObj = new Obj();
    myObj.getThis() === myObj // true
    

    避免使用this,使用 Proxy

    function selfish (target) {
      const cache = new WeakMap();
      const handler = {
        get (target, key) {
          const value = Reflect.get(target, key);
          if (typeof value !== 'function') {
            return value;
          }
          if (!cache.has(value)) {
            cache.set(value, value.bind(target));
          }
          return cache.get(value);
        }
      };
      const proxy = new Proxy(target, handler);
      return proxy;
    }
    const logger = selfish(new Logger());
    
  • 相关阅读:
    2022年最新前端面试题,持续更新
    QtCreator开发环境的安装和配置
    AttributeError: ‘Manager‘ object has no attribute ‘_Manager__name‘
    企业电子招标采购系统源码Spring Boot + Mybatis + Redis + Layui + 前后端分离 构建企业电子招采平台之立项流程图
    Day21力扣打卡
    【C++】函数指针 ① ( 函数三要素 | 函数类型 | 函数指针类型 | 函数类型重命名 )
    【华为机试真题 JAVA】免单统计-100
    mysql5.7.35安装配置教程【超级详细安装教程】
    Mathtype插入word,以及mathtype在word上的卸载
    开源 | 携程 Redis On Rocks 实践,节省 2/3 Redis成本
  • 原文地址:https://blog.csdn.net/qq_35876316/article/details/140356423