JavaScript中的类(ES5+ES6)

ES5

ECMAScript 2015 中引入的 JavaScript 类实质上是 JavaScript 现有的基于原型的继承的语法糖。类语法不会为JavaScript引入新的面向对象的继承模型。

类实际上是个“特殊的函数”,就像你能够定义的函数表达式和函数声明一样,类语法有两个组成部分:类表达式和类声明。

类声明

定义一个类的一种方法是使用一个类声明。要声明一个类,你可以使用带有class关键字的类名(这里是“Rectangle”)。

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

函数声明和类声明之间的一个重要区别是函数声明会提升,类声明不会。

类表达式

一个类表达式是定义一个类的另一种方式。类表达式可以是被命名的或匿名的。赋予一个命名类表达式的名称是类的主体的本地名称。

/* 匿名类 */ 
let Rectangle = class {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};

/* 命名的类 */ 
let Rectangle = class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};

类表达式也同样受到类声明中提到的提升问题的限制。

ES6

在ES6中,class (类)作为对象的模板被引入,可以通过 class 关键字定义类。它可以被看作一个语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法。

ES5与ES6对比

在ES5中定义一个类:

function Person(name) {
 
    this.name = name;
}
 
Person.prototype.sayHello = function(){
 
    return 'Hi, I am ' + this.name;
}

而用ES6的写法重写一下,检测类型发现Person本质上仍然是函数:

class Person {
 
    constructor(name){
        this.name = name;
    }
 
    sayHello(){
        return 'Hi, I am ' + this.name;
    }
}
 
typeof Person; //'function'

调用的方式都是一致的:

var p1 = new Person('zhangsan');   

用ES6定义class中的方法,定义在原型对象上的。与ES5不同的是,这些定义在原型对象的方法是不可枚举的。

ES6类和模块是严格模式下的;不存在变量提升,保证子类父类的顺序;类的继承也有新的写法:

class Female extends Person {
 
    constructor(name){
        super(name); //调用父类的,调用之后,子类才有this
        this.sex = 'boy';
    }
 
    sayHello(){
        return super.sayHello() + ', I am ' + this.sex;
    }
 
}

注意,子类必须在父类的构造函数中调用super(),这样才有this对象,因为this对象是从父类继承下来的。而要在子类中调用父类的方法,用super关键词可指代父类。

ES5中类继承的关系是相反的,先有子类的this,然后用父类的方法应用在this上。

ES6类继承子类的this是从父类继承下来的这个特性,使得在ES6中可以构造原生数据结构的子类,这是ES5无法做到的。

ES6也可以定义类的静态方法和静态属性,静态的意思是这些不会被实例继承,不需要实例化类,就可以直接拿来用。ES6中class内部只能定义方法,不能定义属性。在方法名前加上static就表示这个方式是静态方法,而属性还是按照ES5的方式来实现。

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

ES6中当函数用new关键词的时候,增加了new.target属性来判断当前调用的构造函数。这个有什么用处呢?可以限制函数的调用,比如一定要用new命令来调用,或者不能直接被实例化需要调用它的子类。

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

ES5类中的 实例方法&原型方法&静态方法

1.实例方法只有实例可以调用

2.实例方法可以调用静态方法,不能调用原型方法

3.原型方法可以被实例和构造函数访问到

4.原型方法只可以调用静态方法

5.静态方法只有构造函数可以调用

ES5中定义三种方法及调用关系:

let Animal = function (type) {
  this.type = type
  // 实例方法
  this.drink = function () {
    // 实例方法可调用类的静态方法
    Animal.walk()
    console.log('drink')
  }
}

// 原型方法
Animal.prototype.eat = function () {
  // 原型方法可调用类的静态方法
  Animal.walk()
  console.log('eat food')
}

// 静态方法
Animal.walk = function () {
  console.log('walking')
}

let dog = new Animal('dog')
dog.drink() // walking drink
dog.eat() // walking eat food
Animal.walk() // walking静态方法只能由<类自身&实例方法&原型方法>调用,实例对象无法调用
// dog.walk() // dog.walk is not a function  实例对象无法调用静态方法

ES6类中的 实例方法&原型方法&静态方法

1.实例方法=原型方法 类中直接定义的实例方法默认就是ES5中的原型方法(语法糖)

2.静态方法用static前缀定义

3.静态方法只有构造函数可以调用,原型方法是实例和构造函数都可以调用,是共享的方法。

ES6中定义三种方法及调用关系:

class Animal {
  constructor (type) {
    this.type = type
  }
  // 定义实例对象的方法
  eat () {
    // 使用静态方法,以类形式访问,而非this访问
    Animal.walk()
    console.log('eat food')
    console.log(this.type) // 类的例实例对象的方法可以获取到实例对象的信息,输出dog
  }
  // static 定义类的静态方法
  static walk () {
    console.log('walking')
    console.log(this.type) // 类的静态方法无法获取到实例对象的信息,输出undefined
  }
}
let dog = new Animal('dog')
dog.eat() // walking ; undefined ; eat food ; dog
Animal.walk() // walking ; undefined ;
dog.walk() // dog.walk is not a function
// 得出结论:需要访问实例对象的信息时用实例对象的方法,否则使用static静态方法
// 实例方法就是只有实例可以调用,静态方法只有构造函数可以调用,原型方法是实例和构造函数都可以调用,是共享的方法。
发布了31 篇原创文章 · 获赞 110 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/liuyifeng0000/article/details/104436139