JavaScript 语言中,生成实例对象的传统方法是通过构造函数。 ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。
基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰更像面向对象编程的语法而已。所以ES6 的类,完全可以看作构造函数的另一种写法。
class Point {
//构造函数
constructor(x, y) {
this.x = x; this.y = y;
}
toString() {
console.log('(' + this.x + ', ' + this.y + ')');
}
}
let p = new Point(1,2)
p.toString() //(1, 2)
注意:类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,普通构造函数不用new也可以执行。
实例方法
在类中可以直接定义方法,实际上类的所有方法都定义在类的prototype属性上面。在类的实例上面调用方法,其实就是调用原型上的方法。
class Point {
constructor() { // ... }
toString() { // ... }
toValue() { // ... }
}
constructor 方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。class Point { }
等同于class Point { constructor() {} }
由于类的方法都定义在prototype对象上面,所以类的新方法可以添加在prototype对象上面。Object.assign 方法可以很方便地一次向类添加多个方法。
class Point {
constructor(){ // ... }
}
Object.assign(Point.prototype, {
toString(){},
toValue(){}
});
静态方法
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上 static 关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为 “静态方法”。
class Foo {
static classMethod() {
return 'hello';
}
}
Foo.classMethod() // 'hello'
需要注意的是,如果静态方法包含this关键字,这个this指的是类,而不是实例。
实例属性
类的实例属性可以定义在构造函数中。
class Person{
constructor(id,name,age) {
this.id = id;
this.name = name;
this.age = age;
}
}
静态属性
直接在类上定义的属性称为是静态属性,只能通过类来调用。
class Foo { }
Foo.prop = 1;
Foo.prop // 1
目前,只有这种写法可行,因为 ES6 明确规定,Class 内部只有静态方法,没有静态属性。
对象的继承
我们先来回顾一下 ES5 中对象的原型链继承方法。
function Animal(name,age){
this.name = name
this.age = age
}
function Dog(name,age,gender){
//借用Animal构造函数
Animal.apply(this,arguments)
this.gender = gender
}
Dog.prototype = new Animal()
var dog = new Dog('旺财',6,'male')
console.log(dog instanceof Animal) //true
class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
class Animal {
constructor(name){
this.name = name
}
sayName(){
console.log('my name is',this.name);
}
static sayHello(){
console.log('hello');
}
}
class Dog extends Animal{
constructor(name,age){
//super()代表调用父类的构造函数
super(name)
this.age = age
}
sayAge(){
console.log('my age is',this.age)
}
}
let dog = new Dog('旺财',12)
dog.sayName()
dog.sayAge()
Dog.sayHello()
console.log(dog)
super 这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。
- 函数:
子类B的构造函数之中的super(),代表调用父类的构造函数。 super虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B,因此super()在这里相当于A.prototype.constructor.call(this)
。 - 对象:
在普通方法中,指向父类的原型对象;在静态方法中,指向父类。由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的。
class 作为构造函数的语法糖,同时有 prototype属性和__proto__属性,因此同时存在两条继承链。
class A { }
class B extends A { }
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
- 子类的 __proto__属性,表示构造函数的继承,总是指向父类。
- 子类 prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。