Condivisione della tecnologia

Riepilogo delle classi ES6 (9)

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

La classe in ES6 è uno zucchero sintattico per la programmazione orientata agli oggetti, che fornisce un modo conciso per definire la struttura e il comportamento degli oggetti.

Nel linguaggio JavaScript, il modo tradizionale per generare oggetti istanza è tramite i costruttori. Di seguito è riportato un esempio.

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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Fondamentalmente, la classe ES6 può essere considerata solo uno zucchero sintattico. La maggior parte delle sue funzioni possono essere eseguite da ES5. Il nuovo metodo di scrittura della classe rende solo il metodo di scrittura del prototipo di oggetto più chiaro e più simile alla sintassi della programmazione orientata agli oggetti. Il codice precedente viene riscritto utilizzando le classi ES6, come segue:

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Le classi ES6 possono essere considerate come un altro modo di scrivere costruttori:

class Point {
  // ...
}
typeof Point // "function"
Point === Point.prototype.constructor // true

----------------------------------------------------------------------------
class Point {
  constructor() {
    // ...
  }
  toString() {
    // ...
  }
  toValue() {
    // ...
  }
}
// 等同于
Point.prototype = {
  constructor() {},
  toString() {},
  toValue() {},
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

Caratteristiche principali:

1. Sintassi dichiarativa: utilizza la parola chiave class per dichiarare una classe.
2. Costruttore: utilizza il metodo del costruttore per inizializzare un'istanza di classe.
3. Metodi di istanza: un metodo comune definito all'interno di una classe, utilizzalo per accedere alle proprietà dell'istanza.
4. Metodi statici: Definito utilizzando la parola chiave static e non dipende dall'istanza della classe.
5. Proprietà dell'istanza: inizializza nel costruttore o utilizza la sintassi della dichiarazione del campo (attualmente una proposta della Fase 3).
6. Eredità: implementato utilizzando la parola chiave extends.
7. super parola chiave: chiama il costruttore o il metodo della classe genitore nel costruttore della sottoclasse.
8. getter e setter: utilizzare get e set per definire le funzioni di accesso per le proprietà.
9. Proprietà e metodi privati: utilizzare # per definire proprietà e metodi privati ​​(attualmente una proposta della Fase 3).

1. Definizione e istanziazione di classi di base

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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2. Metodi e proprietà statiche

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

3. Eredità e 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

4. getter e 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.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

Note sulle lezioni

(1) Modalità rigorosa

Internamente, le classi e i moduli sono in modalità rigorosa per impostazione predefinita, quindi non è necessario utilizzare use strict per specificare la modalità di esecuzione. Finché il codice è scritto in una classe o in un modulo, è disponibile solo la modalità rigorosa. Considerando che tutto il codice futuro verrà effettivamente eseguito in moduli, ES6 aggiorna effettivamente l'intero linguaggio alla modalità rigorosa.

(2) Non è prevista alcuna promozione

Le classi non hanno il sollevamento variabile (paranco), che è completamente diverso da ES5.

new Foo(); // ReferenceError
class Foo {}

//不会报错
//因为 Bar 继承 Foo 的时候, Foo 已经有定义了。
//但是,如果存在 class 的提升,上面代码就会报错,
//因为 class 会被提升到代码头部,而 let 命令是不提升的,
//所以导致 Bar 继承 Foo 的时候, Foo 还没有定义。
{
  let Foo = class {};
  class Bar extends Foo {
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

(3) attributo nome

Poiché in sostanza una classe ES6 è solo un wrapper per il costruttore ES5, molte funzionalità della funzione vengono ereditate da Class, incluso l'attributo name.

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

(4) Metodo del generatore

Se un metodo è preceduto da un asterisco (*), significa che il metodo è una funzione 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 循环会自动调用这个遍历器。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

(5) Il punto di questo

Se un metodo di classe lo contiene, punta a un'istanza della classe per impostazione predefinita. Bisogna però fare molta attenzione, poiché questo metodo potrebbe causare errori se utilizzato da solo.

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Evita di usarlo, collegalo nel costruttore:

class Logger {
  constructor() {
    this.printName = this.printName.bind(this);
  }
  // ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Evita di usarlo, usa le funzioni freccia:

class Obj {
  constructor() {
    this.getThis = () => this;
  }
}
const myObj = new Obj();
myObj.getThis() === myObj // true
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Evita di usarlo, usa 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());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18