ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class
关键字,可以定义类。
基本上,ES6 的class
可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class
写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
//传统面向对象写法
function Person(name,age){ // 类、构造函数
this.name = name;
this.age = age;
}
Person.prototype.showName = function(){
return this.name;
};
Person.prototype.showAge = function(){
return this.age;
};
var p1 = new Person('allen',28);
var p2 = new Person('nana',24);
console.log(p1.showName()); // allen
console.log(p2.showAge()); // 24
console.log(p1.showName === p2.showName); // true
console.log(p1.constructor == Person); // true
//ES6面向对象写法
class Person{
// 构造器
constructor(name,age){
this.name = name;
this.age = age;
}
showName(){
return this.name;
}
showAge(){
return this.age;
}
}
var p1 = new Person('aaa',18);
var p2 = new Person('bbb',20);
console.log(p1.name); // aaa
console.log(p1.showName()); // aaa
console.log(p2.showAge()); // 20
console.log(p1.showAge == p2.showAge); // true
console.log(p1.constructor == Person); // true
面向对象class给默认值
class Person{
// 构造器
constructor(name='default',age=0){
this.name = name;
this.age = age;
}
showName(){
return this.name;
}
showAge(){
return this.age;
}
}
var p1 = new Person();
console.log(p1.name); // 构造器里面给的默认值 default
console.log(p1.age); // 构造器里面给的默认值 0
继承
传统写法原型继承
// 定义一个动物类
function Animal (name) {
// 属性
this.name = name || 'Animal';
// 实例方法
this.sleep = function(){
console.log(this.name + '正在睡觉!');
}
}
// 原型方法
Animal.prototype.eat = function(food) {
console.log(this.name + '正在吃:' + food);
};
//通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
function Cat(name){
Animal.apply(this,arguments);
//this.name = name;
}
Cat.prototype = new Animal();
// 组合继承也是需要修复构造函数指向的。
//Cat.prototype.constructor = Cat;
// Test Code
var cat = new Cat("Dog");
cat.sleep()
cat.eat("骨头");
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true
ES6中面向对象实现类继承
class Animal{
constructor(name='Animal'){
this.name=name;
}
eat(food){
console.log(this.name+'正在吃'+food);
}
}
class Cat extends Animal{
constructor(name,color){
super(name);
this.color=color;
}
showColor(){
return this.color;
}
}
var cat=new Cat("cat","white");
cat.eat("鱼")
console.log(cat.showColor());
Class可以通过extends关键字实现继承,这比ES5通过修改原型链实现继承要清晰和方便很多。
class Point{
}
class ColorPoint extends Point{
}
上面的代码定义了一个ColorPoint类,该类通过extends关键字,继承了Point类的所有属性和方法。
class Point{
constructor(x,y){
this.x=x;
this.y=y
}
toString(){
return '('+this.x+','+this.y+')';
}
}
class ColorPoint extends Point{
constructor(x,y,color){
super(x,y) ; // 调用父类的constructor(x,y)
this.color=color;
}
toString(){
return this.color+' '+super.toString(); // 调用父类的toString()
}
}
子类必须在constructor
方法中调用super
方法,否则新建实例时会报错。这是因为子类自己的this
对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super
方法,子类就得不到this
对象。
所以 它们的位置不能颠倒。
生成子类实例的代码:
let cp=new ColorPoint(10,24,'green');
cp instanceof ColorPoint // true
cp instanceof Point // true
父类的静态方法也会被子类继承
class A{
static sayHello(){
console.log("hello world!")
}
}
class B extends A{
}
B.hello() // hello world
super 关键字
super
这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。
第一种情况,super
作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super
函数。
class A {}
class B extends A {
constructor() {
super();
}
}
注意,super
虽然代表了父类A
的构造函数,但是返回的是子类B
的实例,即super
内部的this
指的是B
,因此super()
在这里相当于A.prototype.constructor.call(this)
。
class A {
constructor() {
console.log(new.target.name);
}
}
class B extends A {
constructor() {
super();
}
}
new A() // A
new B() // B
第二种情况,super
作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
class A {
p() {
return 2;
}
}
class B extends A {
constructor() {
super();
console.log(super.p()); // 2
}
}
let b = new B();
上面代码中,子类B
当中的super.p()
,就是将super
当作一个对象使用。这时,super
在普通方法之中,指向A.prototype
,所以super.p()
就相当于A.prototype.p()
。
这里需要注意,由于super
指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super
调用的。