Partage de technologie

Résumé du cours ES6 (9)

2024-07-12

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

La classe dans ES6 est un sucre syntaxique pour la programmation orientée objet, offrant un moyen concis de définir la structure et le comportement des objets.

Dans le langage JavaScript, la manière traditionnelle de générer des objets d'instance consiste à utiliser des constructeurs. Ci-dessous un exemple.

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

Fondamentalement, la classe ES6 peut être considérée comme un simple sucre syntaxique. La plupart de ses fonctions peuvent être réalisées par ES5. La nouvelle méthode d'écriture de classe ne fait que rendre la méthode d'écriture du prototype d'objet plus claire et plus proche de la syntaxe de la programmation orientée objet. Le code ci-dessus est réécrit à l'aide des classes ES6, comme suit :

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

Les classes ES6 peuvent être considérées comme une autre façon d’écrire des constructeurs :

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

Caractéristiques principales:

1. Syntaxe déclarative: Utilisez le mot-clé class pour déclarer une classe.
2. Constructeur: utilisez la méthode constructeur pour initialiser une instance de classe.
3. Méthodes d'instance: Une méthode courante définie dans une classe, utilisez-la pour accéder aux propriétés de l'instance.
4. Méthodes statiques: Défini à l'aide du mot-clé static et ne dépend pas de l'instance de la classe.
5. Propriétés des instances: Initialisez dans le constructeur ou utilisez la syntaxe de déclaration de champ (actuellement une proposition de l'étape 3).
6. Héritage: Implémenté à l'aide du mot-clé extends.
7. super mot-clé: Appelez le constructeur ou la méthode de la classe parent dans le constructeur de la sous-classe.
8. getter et setter: Utilisez get et set pour définir les accesseurs pour les propriétés.
9. Propriétés et méthodes privées : utilisez # pour définir des propriétés et des méthodes privées (actuellement une proposition d'étape 3).

1. Définition et instanciation de classe de 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. Méthodes et propriétés statiques

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. Héritage et 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 et 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

Notes sur les cours

(1) Mode strict

En interne, les classes et les modules sont en mode strict par défaut, il n'est donc pas nécessaire d'utiliser use strict pour spécifier le mode d'exécution. Tant que votre code est écrit dans une classe ou un module, seul le mode strict est disponible. Étant donné que tout le code futur s'exécutera en fait dans des modules, ES6 met en fait à niveau l'ensemble du langage en mode strict.

(2) Il n'y a pas de promotion

Les classes n'ont pas de levage variable (hoist), ce qui est complètement différent de l'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) attribut de nom

Puisqu'une classe ES6 n'est essentiellement qu'un wrapper pour le constructeur ES5, de nombreuses fonctionnalités de la fonction sont héritées par Class, y compris l'attribut name.

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

(4)Méthode du générateur

Si une méthode est précédée d'un astérisque (*), cela signifie que la méthode est une fonction Générateur.

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) Le but de ceci

Si une méthode de classe contient cela, elle pointe par défaut vers une instance de la classe. Cependant, vous devez être très prudent, car cette méthode peut provoquer des erreurs si elle est utilisée seule.

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

Évitez d'utiliser ceci, liez ceci dans le constructeur :

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

Évitez d'utiliser ceci, utilisez les fonctions fléchées :

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

Évitez d'utiliser ceci, utilisez 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