ES6中的class类

一.ES6中的类是面向对象编程的语法糖

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);

//上下两段代码意义一样

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}

二.在class中定义方法,其实就是在构造函数的原型链上添加方法

class Point {
  constructor() {
    // ...
  }

  toString() {
    // ...
  }

  toValue() {
    // ...
  }
}

// 等同于

Point.prototype = {
  constructor() {},
  toString() {},
  toValue() {},
};

三.类的所有内部定义的方法都不可被枚举(与es5不同)

class Point {
  constructor(x, y) {
    // ...
  }

  toString() {
    // ...
  }
}

Object.keys(Point.prototype)
// []
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]


//-------------------------------------------------------------------------ES5中

var Point = function (x, y) {
  // ...
};

Point.prototype.toString = function () {
  // ...
};

Object.keys(Point.prototype)
// ["toString"]
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]

四.constructor方法

1.constructor方法是类的默认方法,在使用 new 命令生成实例时会自动调用此方法,如果constructor方法没有被定义,则默认添加一个空的constructor方法

2.constructor方法默认返回实例对象(也就是this),也可以自己设置返回的对象

下面的代码设置了constructor方法的返回值,所以通过new操作符生成的对象不是Foo的实例

class Foo {
  constructor() {
    return Object.create(null);
  }
}

new Foo() instanceof Foo
// false

3.其实实例属性可以不写在constructor内,也可以写在类的顶层,此时_count属性与取值函数value以及increment在同一级,不需要在前面加this。与在constructor中定义意义一样,但看起来比较简洁

class IncreasingCounter {
  _count = 0;
  get value() {
    console.log('Getting the current value!');
    return this._count;
  }
  increment() {
    this._count++;
  }
}

五.class必须用new操作符调用(与ES5不同,构造函数可直接调用)

1.不使用new操作符调用会报错

六.实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)

//定义类
class Point {

  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }

}

var point = new Point(2, 3);

point.toString() // (2, 3)

point.hasOwnProperty('x') // true
point.hasOwnProperty('y') // true
point.hasOwnProperty('toString') // false
point.__proto__.hasOwnProperty('toString') // true

/*
    x,y两个属性定义在this上,因此hasOwnProperty返回true
    而toString方法在原型链上,因此hasOwnProperty返回false
*/

七.类的所有实例共享一个原型对象

var p1 = new Point(2,3);
var p2 = new Point(3,2);

p1.__proto__ === p2.__proto__
//true

八.在class中可以使用getset关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为

class MyClass {
  constructor() {
    // ...
  }
  get prop() {
    return 'getter';
  }
  set prop(value) {
    console.log('setter: '+value);
  }
}

let inst = new MyClass();

inst.prop = 123;
// setter: 123

inst.prop
// 'getter'

九.存值函数和取值函数是设置在属性的 Descriptor 对象上的

class CustomHTMLElement {
  constructor(element) {
    this.element = element;
  }

  get html() {
    return this.element.innerHTML;
  }

  set html(value) {
    this.element.innerHTML = value;
  }
}

/*
    这个方法用于返回一个对象上的自有属性的属性描述符
    这个方法接受两个参数,第一个为要查询的对象,第二个为查询对象上的属性名称
    这个方法返回有两种结果,如果指定的属性在对象上存在,则返回他的属性描述符,否则返回undefined
*/

var descriptor = Object.getOwnPropertyDescriptor(
  CustomHTMLElement.prototype, "html"
);

//  in操作符,如果指定的属性在指定的对象或其原型链上,则返回true

"get" in descriptor  // true
"set" in descriptor  // true

十.class不存在变量提升(与ES5不同)

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

十一.静态方法(静态属性与静态方法定义方法一致)

1.类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上 static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”

class Foo {
  static classMethod() {
    return 'hello';
  }
}

Foo.classMethod() // 'hello'

var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function

2.如果在静态方法中使用到了this,这个this指向类本身,而不是实例(且静态方法与非静态方法可以重名)

class Foo {
  static bar() {
    this.baz();
  }
  static baz() {
    console.log('hello');
  }
  baz() {
    console.log('world');
  }
}

Foo.bar() // hello

十二.私有属性和私有方法

1.有时有一些属性和方法我们希望只允许在类的内部使用,此时他们就是私有属性和私有方法

可以在属性或方法前加#,此时该属性或方法只能在类中被取到

2.私有属性和私有方法也可以被设置setter和getter,也可以在之前加static被设置为静态的私有属性和私有方法

//私有属性
class IncreasingCounter {
  #count = 0;
  get value() {
    console.log('Getting the current value!');
    return this.#count;
  }
  increment() {
    this.#count++;
  }
}

const counter = new IncreasingCounter();
counter.#count // 报错
counter.#count = 42 // 报错

//私有方法
class Foo {
  #a;
  #b;
  constructor(a, b) {
    this.#a = a;
    this.#b = b;
  }
  #sum() {
    return this.#a + this.#b;
  }
  printSum() {
    console.log(this.#sum());
  }
}

十三.静态块(ES2022)

1.有时定义的一些属性依赖于另外一个静态属性,此时可以把他放在class中的静态块中,静态块中的代码只会在创建类时执行一次,用于对静态属性进行初始化。解决了传统初始化方法中将逻辑写在类外部或将初始化代码放在constructor中每次实例化类时都会被执行一次的问题

//使用静态块

class C {
  static x = ...;
  static y;
  static z;

  static {
    try {
      const obj = doSomethingWith(this.x);
      this.y = obj.y;
      this.z = obj.z;
    }
    catch {
      this.y = ...;
      this.z = ...;
    }
  }
}

//不使用静态块

class C {
  static x = 234;
  static y;
  static z;
}

try {
  const obj = doSomethingWith(C.x);
  C.y = obj.y
  C.z = obj.z;
} catch {
  C.y = ...;
  C.z = ...;
}

2.静态块中不能有return语句

3.静态块中可以使用类名或this指代当前类

class C {
  static x = 1;
  static {
    this.x; // 1
    // 或者
    C.x; // 1
  }
}

十四.new.target

1.new.target命令可以用于判断一个构造函数是否是被new操作符执行的。若是被new或Reflect.construct()调用的,则它返回这个构造函数,否则返回undefined。

function Person(name) {
  if (new.target !== undefined) {
    this.name = name;
  } else {
    throw new Error('必须使用 new 命令生成实例');
  }
}

// 另一种写法
function Person(name) {
  if (new.target === Person) {
    this.name = name;
  } else {
    throw new Error('必须使用 new 命令生成实例');
  }
}

var person = new Person('张三'); // 正确
var notAPerson = Person.call(person, '张三');  // 报错

2.在class内部调用new.target会返回当前的class,但若是子类继承父类时,会返回子类

class Rectangle {
  constructor(length, width) {
    console.log(new.target === Rectangle);
    this.length = length;
    this.width = width;
  }
}

var obj = new Rectangle(3, 4); // 输出 true


//被子类继承

class Rectangle {
  constructor(length, width) {
    console.log(new.target === Rectangle);
    // ...
  }
}

class Square extends Rectangle {
  constructor(length, width) {
    super(length, width);
  }
}

var obj = new Square(3); // 输出 false

3.妙用:可以使用new.target写出只能被继承而不能被实例化的class

class Shape {
  constructor() {
    if (new.target === Shape) {
      throw new Error('本类不能实例化');
    }
  }
}

class Rectangle extends Shape {
  constructor(length, width) {
    super();
    // ...
  }
}

var x = new Shape();  // 报错
var y = new Rectangle(3, 4);  // 正确

你可能感兴趣的:(javascript,前端,开发语言)