2.4JavaScript引用类型之类(Class)

1.类声明

类是一种“特殊的函数”,就像函数声明定义方式和函数表达式定义方式一样,定义类的方式也有两种:类声明定义方式和类表达式定义方式。

//类声明定义方式
class Rectangle {
  //类体
}
//类表达式定义方式

//命名类
let Rectangle = class Rectangle2 {
  //类体
};
console.log(Rectangle.name);
//输出类名称: "Rectangle2"

//未命名/匿名类
let Rectangle = class {
  //类体
};
console.log(Rectangle.name);
//输出类名称: "Rectangle"

2.继承

一个类只能继承自一个父类,不可以继承自多个父类。

class 子类名 extends 父类名 {
  //类体
}

3.抽象(Abstract)类

虽然ECMAScript没有专门支持抽象类的语法,但是我们可以通过new.target来实现抽象类。通过在实例化时检测new.target是不是抽象基类,可以阻止对抽象基类的实例化。

class Vehicle {
  constructor() {
    console.log(new.target);  
    if (new.target === Vehicle) {
      throw new Error('Vehicle cannot be directly instantiated');
    }
  }
}
//继承
class Bus extends Vehicle {}

//class Bus {}
new Bus();

//Error: Vehicle cannot be directly instantiated
new Vehicle();

4.重写(Override)

可以在抽象父类构造函数中进行检查,可以要求子类必须定义某个方法。

class Vehicle {
  constructor() {
    if (new.target === Vehicle) {
      throw new Error('Vehicle cannot be directly instantiated');
    }

    if(!this.foo) {
      throw new Error('Inheriting class must define foo()');
    }

    console.log('success!');
  }
}

//子类
class Bus extends Vehicle {
  foo() {};
}

//子类
class Van extends Vehicle {}

//success
new Bus();
//Error:Inheriting class must define foo()
new Van();

5.构造函数

一个类只能有一个构造函数。

class 类名 {
  constructor() {
    //构造函数体
  }
}

6.静态初始化块(Static Initialization Blocks)

一个类可以有多个静态初始化块。

构造函数是用于实例字段的初始化,而静态初始化块(Static Initialization Blocks)是用于静态字段的初始化。

class 类名 {
  static {
    //静态初始化块体
  }
}
//示例

class ClassWithStaticInitializationBlock {
  static staticProperty1 = 'Property 1';
  static staticProperty2;
  static {
    this.staticProperty2 = 'Property 2';
  }
}

console.log(ClassWithStaticInitializationBlock.staticProperty1);
//输出
"Property 1"

console.log(ClassWithStaticInitializationBlock.staticProperty2);
//输出
"Property 2"

7.创建对象

//类发明之前的对象创建方式一

//字面量创建方式
let 对象 = {
  字段名: 值,
  方法名: function() {
    //方法体
  },
  //方法简写
  方法名() {
    //方法体
  }
};

//示例
let person = {
  name: "张三",
  age: 28,
  sayName: function() {
    console.log(this.name);
  },
  //方法简写
  sayName() {
    console.log(this.name);
  }
};
//类发明之前的对象创建方式二

//Object()构造函数创建方式
//Object()可以调用或不调用new,两者一样。

let 对象 = new Object();
对象.字段名 = 值;
对象.方法名 = function() {
  //方法体
};

//示例
let person = new Object();  
person.name = "张三";
person.age = 28;
person.sayName = function() {
  console.log(this.name);
};
//类发明之后的对象创建方式
//类名构造函数创建对象方式
//如果没有参数传递,可省略括号,一般不推荐省略。
let 对象 = new 类名();
let 对象 = new 类名(参数);

对象属性名称可以是字符串、数字、符号。如果对象属性名称不是合法的 Javascript 标识符,它必须用 "" 包裹。

//属性名为car1字符串和数字7
let car = { car1: "Jeep", 7: "Mazda" };
//属性名为不合法的标识符
let unusualPropertyNames = {
  "": "An empty string",
  "!": "Bang!"
}

8.访问成员

//类定义方式使用.访问
类.静态字段
类.静态方法()
对象.实例字段
对象.实例方法()

//对象字面量方式使用[]访问
//单引号或双引号都可以,没区别
对象['属性名']
对象['方法名']

8.重载(Overload)

JavaScript不支持重载,如果定义了两个同名函数,则后定义的会覆盖先定义的。可以通过检查参数的类型和数量,然后执行不同的逻辑来模拟函数重载。

9.Getter/Setter

Getter/Setter支持静态Getter/Setter和实例Getter/Setter。

Getter/Setter访问控制默认是 public,另外也支持 private

let person = {
  firstName: "Bill",
  lastName : "Gates",
  language : "en",

  get lang() {
    return this.language;
  }
  
  set lang(lang) {
    this.language = lang;
  }
};

10.可计算属性名

ECMAScript的对象的键往往是确定的,如果想要实现对象的键根据实际情况赋予不同的键的话,那就得用 可计算属性名 这个功能,把需要计算的表达式放在方括号[]里面。

在ECMA262-5(2009)里面的做法是:

const nameKey = "name";
const ageKey = "age";
const jobKey = "job";

let person = {};
person[nameKey] = 'Matt';
person[ageKey] = 27;
person[jobKey] = 'Software engineer';

console.log(person);
//输出
{ name: 'Matt', age: 27, job: 'Software engineer' }

从ECMA262-6(2015)开始添加了 可计算属性名 这个功能,这里这样做:

const nameKey = "name";
const ageKey = "age";
const jobKey = "job";

let person = {
  [nameKey] : 'Matt',
  [ageKey] : 27,
  [jobKey] : 'Software engineer'
};

console.log(person);
//输出
{ name: 'Matt', age: 27, job: 'Software engineer' }

11.super关键字

使用场景调用方式
调用父类的构造函数super(参数),必须在this关键字和构造函数返回之前
调用父类的属性(支持静态字段、静态方法、实例方法,不支持实例字段)super.prop 和 super[expr]两种方式都可以
调用父对象字面量的属性(字段、方法)super.prop 和 super[expr]两种方式都可以
//调用父类的构造函数
class Foo {
  constructor(name) {
    this.name = name;
  }
}

class FooBar extends Foo {
  constructor(name, index) {
    super(name);
    this.index = index;
  }
}
//调用父类的属性
class Foo {
  static baseStaticField = 90;

  static logNbSides() {
    return "I have 4 sides";
  }
  
  getNameSeparator() {
    return '-';
  }
}

class FooBar extends Foo {

  static extendedStaticField = super.baseStaticField;

  static logDescription() {
    return super.logNbSides();
  }
  
  getFullName() {
    return super.getNameSeparator();
  }
}
//调用父对象字面量的属性
const obj1 = {
  prop: 1,
  method1() {
    console.log("method 1");
  }
};

const obj2 = {
  myParent() {
    console.log(super.prop);
  },
  method2() {
    super.method1();
  }
};

Object.setPrototypeOf(obj2, obj1);

12.this关键字

在非严格模式下,this 的值始终是当前对象的引用。

在严格模式下,this 的值根据其所在的上下文而有所不同:全局、类、函数。

原创文章,作者:huoxiaoqiang,如若转载,请注明出处:https://www.huoxiaoqiang.com/javascript/javascriptlang/4729.html

(0)
上一篇 2020年9月3日 02:23
下一篇 2020年9月5日 02:25

相关推荐

发表回复

登录后才能评论