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属性。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐