ES6 class

1 class declarations 可以用以下三种方式声明class类,必须先声明在使用;每一个使用class方式定义的类默认都有一个constructor函数, 这个函数是构造函数的主函数, 该函数体内部的this指向生成的实例

An important difference between function declarations and class declarations is that function declarations are hoisted(变量提升) and class declarations are not. You first need to declare your class and then access it,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var Rect = class Rect {
constructor(height,width){
this.height = height ;
this.width = width ;
}
}
var Rect = class {
constructor(height,width){
this.height = height ;
this.width = width ;
}
}
class Rect {
constructor(height,width){
this.height = height ;
this.width = width ;
}
}

2 对比ES5和ES6中的差别

constructor内定义的方法和属性是实例对象自己的,而constructor外定义的方法和属性则是所有实例对象可以共享的。

2.1 创建类的过程差别

ES5中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function RectAngle(height,width){
this.height = height ;
this.width = width ;
}
RectAngle.prototype.getArea = function(){
return this.calcArea();
}
RectAngle.prototype.calcArea = function(){
return this.height*this.width;
}
console.dir(RectAngle) ;//输出下构造函数
var rectAngle = new RectAngle(10,10);
console.log(rectAngle);
console.log(rectAngle.getArea());
console.log(rectAngle.__proto__ === Rectangle.prototype); //true

ES6中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Rect {
constructor(height,width){
this.height = height ;
this.width = width ;
}
//定义原型方法 类似于ES5中的Rect.prototype.getArea = function(){};
getArea(){
return this.calcArea();
}
calcArea(){
return this.height*this.width ;
}
}
console.dir(Rect) ;
const square = new Rect(10,10);
console.log(square);
console.log(square.getArea());
console.log(square.__proto__ === Rect.prototype); //true
//class类的prototype属性和其实例化对象的__proto__ 全等,这点和ES5中的构造函数prototype和其实例化对象的__proto__全等一致

2.2 类中的方法独立调用的时候,函数内部this指向不同

ES5中:原型上的方法被独立调用,非严格模式下,this会指向window,严格模式下指向undefined

1
2
3
4
5
6
7
8
9
10
11
function RectAngle(height,width){
this.height = height ;
this.width = width ;
}
RectAngle.prototype.getArea = function(){
return this;
}
var rectAngle = new RectAngle(10,10);
let getArea = rectAngle.getArea;
console.log(getArea());//window

ES6中: static方法或者原型上的方法被独立调用的时候,无论是否严格模式,其this指向都是undefined

1
2
3
4
5
6
7
8
9
10
11
12
13
class Rect {
constructor(height,width){
this.height = height ;
this.width = width ;
}
//定义原型方法 类似于ES5中的Rect.prototype.getArea = function(){};
getArea(){
return this;
}
}
const square = new Rect(10,10);
let getArea = square.getArea;
console.log(getArea());//undefined

2.3 class类中static声明的方法不能被实例调用,也不会出现在实例化对象上 ; 可以直接通过类名调用;

The static keyword defines a static method for a class. Static methods are called without instantiating )their class and cannot be called through a class instance. Static methods are often used to create utility functions for an application.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Rect {
constructor(height,width){
this.height = height ;
this.width = width ;
}
getArea(){ //这个是Rect类的属性prototype上的方法
return this.calcArea();
}
calcArea(){
return this.height*this.width ;
}
//静态方法为class类定义了一个方法,该方法不能再class类的实例对象上使用
static shortHBW (H,W){
return H-W ;
}
}
console.log(Rect);//static方法其实就是class类的属性
console.log(Rect.shortHBW(21,12) );//9 //static方法直接通过类名可以直接调用
console.log(Rect.getArea() ); //prototype method 不能直接通过类名调用

2.4 先来看下extends关键字的作用 class类实现继承的根本原因就是通过extends关键字,将子类的 proto 属性指向父类构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
class Dog { //没有继承的情况下
speak1() {
console.log(this.name + ' barks.');
}
}
console.dir(Dog);
console.dir(Animal);
console.log(Dog.__proto__ === Animal);//fasle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
class Dog extends Animal{ //extends的作用 是将子类(函数) 的__proto__属性指向父类(函数)
,由此可以实现继承父类所有的属性和方法
speak1() {
console.log(this.name + ' barks.');
}
}
console.dir(Dog);
console.dir(Animal);
console.log(Dog.__proto__ === Animal);//true 这个是extends关键字的作用核心
var dog = new Dog();
console.log(dog);
console.log(dog.__proto__ === Dog.prototype);//true 实例化的对象的原型属性指向的是构造函数的原型,而构造函数的原型属性指向的是父类构造函数

2.5 constructor 方法,以及super关键字

2.5.1 The constructor method is a special method for creating and initializing an object created with a class; There can only be one special method with the name “constructor” in a class. A SyntaxError will be thrown if the class contains more than one occurrence of a constructor method.

A constructor can use the super keyword to call the constructor of a parent class.

constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。constructor函数默认返回实例对象

  • 当创建一个新类的时候,如果没有显式的添加constructor函数,那么默认将会添加一个空的constructor函数
1
2
3
4
5
6
7
8
9
class Parent {
}
//等价于
class Parent {
constructor(){
}
}
  • 当子类继承父类的时候,如果子类没有显式的添加constructor函数,那么默认将会添加一个constructor,并且自动调用super方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Parent(){
constructor(x){
this.x = x
}
}
class Child extends Parent{
}
//等价于
class Child extends Parent{
constructor(){
super()
//此处的super虽然代表了父类的构造函数,但是其返回的是子类Child的实例对象,即super内部的this指的是Child类,相当于super( ) ==> Parent.prototype.constructor.call(this)
}
}

2.5.2 super有三种作用, 第一是作为构造函数直接调用第二种是作为父类实例, 第三种是在子类中的静态方法中调用父类的静态方法;super关键字,它指代父类的实例(即父类的this对象)。子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Cat {
constructor(name,color) {
this.name = name;
this.color = color;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
class Lion extends Cat {
constructor(name,color,age){
super(name,color);//第一个作用,作为构造函数直接调用,必须先调用,用来确定子类实例对象的this指向
this.age = age ;
}
speak() {
super.speak();//第二个作用,super 作为父类实例调用父类的方法
console.log(this.name + ' roars.');
}
}
var lion = new Lion("JErry","white",12);
lion.speak();

2.6 classs类中的 get 和 set 对某个属性设置存值函数和取值函数, 拦截该属性的存取行为

下面这个栗子是对name属性的设置值以及获取值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class People {
constructor(name) { //构造函数
this.name = name;
}
get name() {
return this._name.toUpperCase();
}
set name(value) {
this._name = value;
}
sayName() {
console.log(this.name);
}
}
var p = new People("tom");
console.log(p);
console.log(p.name); //TOM
console.log(p._name); //tom
p.sayName(); //TOM