ES6新特性实现面向对象编程,详解class语法定义类(下)

简介: 接上文。

三、class的继承


继承是面向对象编程中一个非常重要的概念,那什么是继承呢?


(1)继承的概念


继承就是使一个类获得另一个类的属性和方法。就好比一个手艺精湛的师傅传授给你他所有的毕生绝学,那么就相当于你继承了他,此时你既学会了你师傅教你的技能,同时你也一定有属于自己的技能,这不是从你师傅那学来的。


(2)ES5中实现继承


其实在ES5中是通过修改原型链实现继承的,我们可以来看一下简单的例子


// 创建构造函数 Parent
function Parent() {
 // 定义了实例对象属性 name1
 this.name1 = 'parent'
}
// 为 Parent原型定义方法 show1
Parent.prototype.show1 = function() {
 console.log('我是Parent的show1方法')
}
// 创建构造函数 Child
function Child() {
 this.name2 = 'child'
}
// 将构造函数 Child的原型设置成 Parent的实例对象
Child.prototype = new Parent()
// 为Child原型定义方法 show2
Child.prototype.show2 = function() {
 console.log('我是Child的show2方法')
}
// 生成实例对象 child
var child = new Child()
console.log(child.name1)          // parent
console.log(child.name2)          // child
child.show1()                     // 我是Parent的show1方法
child.show2()                     // 我是Child的show2方法


我们可以看到,我们通过改变构造函数 Child的原型 prototype为构造函数 Parent生成的实例对象,实现了继承,即通过构造函数 Child生成的实例对象具有 Parent中定义的属性name1和方法show1,同时也具有属于自己的属性name2和方法show2


(3)ES6中class实现继承


ES5中实现继承的写法显然有些麻烦,所以在 class类中,我们可以通过关键字 extends来实现继承


我们来改写一下ES5中的继承实现


class Parent{
    constructor() {
        this.name1 = 'parent'
    }
    show1() {
        console.log('我是Parent的show1方法')
    }
}
// Child类 继承 Parent类
class Child extends Parent{
    constructor() {
        super();
        this.name2 = 'child'
    }
    show2() {
        console.log('我是Child的show2方法')
    }
}
var child = new Child()
console.log(child.name1)          // parent
console.log(child.name2)          // child
child.show1()                     // 我是Parent的show1方法
child.show2()                     // 我是Child的show2方法


继承得实现整体上看上去非常得简洁


在上述代码中,我们看到了,我们在定义 Child类时用到了关键字 extends,申明了 Child类继承Parent类,同时在 Child类得 constructor函数中调用了 super函数。仅仅用两个关键字就实现了继承,这里我们要对 super进行详细得讲解


(4)super


在ES6中规定了,在子类继承了父类以后,必须先在子类的 constructor函数中调用 super函数,其表示的就是父级的 constructor函数,作用就是为子类生成 this对象,将父类的属性和方法赋值到子类的 this上。因此,若没有调用 super函数,则子类无法获取到 this对象,紧接着就会报错


class A{
    constructor() {
        this.name1 = 'A'
    }
}
class B extends A{
    constructor() {
        this.name2 = 'B'
    }
}
var b = new B()
/*
        this.name2 = 'B'
        ^
ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
*/


上述代码中,B类继承 A类,但 B类的 constructor函数中没有调用 super函数,因此没有生成 this对象,所以 this.name2 = 'B'就报错了


若子类省略了 constructor函数,则默认会帮你调用 super函数的


class A{
    constructor() {
        this.name1 = 'A'
    }
}
class B extends A{
}
var b = new B()
// 没有报错


super()代表的是父类的构造函数,其实 super还可以作为对象使用,即不作为函数调用。当 super在子类的普通方法内时,指向的是父类的原型对象;在子类的静态方法内时,指向的时父类


class A{
 show1() {
  console.log('我是A类的show1方法')
 }
}
class B extends A{
 constructor() {
  super()
 }
 show2() {
  super.show1()
 } 
}
var b = new B()
b.show2()              // 我是A类的show1方法


上述代码,B类继承 A类,其中 A类有一个 show1方法,是写在其原型 A.prototype上的,而在 B类的 show2方法中调用了 super.show1(),我们说过 super在普通的方法中指向的是父类的原型对象,所以 super.show1() 相当于 A.prototype.show1()


我们再来看一个 super在子类的静态方法中的例子


class A{
    static hide1() {
        console.log('我是A类的hide1方法')
    }
}
class B extends A{
    constructor() {
        super()
    }
    static hide2() {
        super.hide1()
    }
}
B.hide2()                     // 我是A类的hide1方法


上述代码,B类继承 A类,B类在其静态方法 hide2中调用了 super.hide1(),因为 super在静态方法中指向的是父类,所以 super.hide1() 就相当于 A.hide1()


说到静态方法,其实类的继承,也是可以继承静态方法的


class A{
 static show() {
  console.log('我是A类的show方法')
 }
}
class B extends A{
}
B.show()                    // 我是A类的show方法


还需要注意的是,当我们在子类的普通方法中通过 super调用父类的方法时,方法中的 this指向的是当前子类的实例对象


class A {
    constructor() {
        this.name = 'Jack'
    }
    show1() {
        console.log(this.name)
    }
}
class B extends A{
    constructor() {
        super();
        this.name = 'Lpyexplore'
    }
    show2() {
        super.show1()
    }
}
var b = new B()
b.show2()                 // Lpyexplore


那么,当我们在子类的静态方法中通过 super调用父类的方法时,方法中的 this指向的是子类,而不是子类的实例对象


class A {
    constructor() {
        this.x = 1
    }
    static show1() {
        console.log(this.x)
    }
}
class B extends A{
    constructor() {
        super();
        this.x = 2
    }
    static show2() {
        super.show1()
    }
}
B.show2()                 // undefined
B.x = 3
B.show2()                 // 3


上述代码中,我们在 B类的静态方法 show2中通过 super调用了 A类的静态方法 show1,执行代码 console.log(this.x),此时的 this指向的是 B类,但因为 B类的 constructor函数中定义的属性 x是定义在 B类的实例对象上的,所以 this.x 返回的是 undefined


所以我们在 B类上定义一个属性 x并且值为 3,此时再此调用 B.show2(),返回的就是 3了。


(5)prototype和__proto__


class类中有两个属性,分别表示着一条继承链,即 prototype__proto__

子类的__proto__总是指向父类;子类的 prototype__proto__总是指向父类的原型


我们来验证一下


class A{}
class B extends A{}
console.log(B.__proto__ === A)                     // true
console.log(B.prototype.__proto__ === A.prototype) // true


四、class类的补充


对于 class类还有几点需要补充以下


(1)不存在变量提升


构造函数本身就是个函数,存在变量提升,所以通过构造函数生成实例对象时,可以将构造函数写在生成实例对象的代码后面


var person = new Person()
function Person() {
 this.name = 'Lpyexplore'
}
// 没有报错


虽然 class类的数据类型也属于 function,但是它是不存在变量提升的,即不可以在申明类之前生成实例对象,否则就会报错


var person = new Person()
class Person{}
/*
报错:
var person = new Person()
             ^
ReferenceError: Cannot access 'Person' before initialization
*/


(2)new.target


class类必须通过 new来生成实例对象,因此ES6引入了一个新的属性 new.target,该属性一般用于 constructor函数中,表示通过关键字 new作用的构造函数的名称,若不是通过 new命令调用的,则返回 undefined


class A{
 constructor() {
  if(new.target === 'undefined') {
   console.log('请通过关键字new调用')
  } else {
   console.log(new.target)
  }
 }
}
var a = new A()              // [class A]


当子类继承父类,并调用父类的 constructor函数时,new.target返回的是子类的构造函数名以及继承自哪个父类


class A{
 constructor() {
  console.log(new.target)
 }
}
class B extends A{
 constructor() {
  super()
 }
}
var b = new B()               // [class B extends A]


五、结束语


好了,ES6的 class语法就讲到这里,希望这篇文章能帮助到大家。


相关文章
|
7月前
|
JavaScript 前端开发 Java
TypeScript 类 第一章【类,继承,修饰符】
TypeScript 类 第一章【类,继承,修饰符】
67 1
|
7月前
|
存储 设计模式 JavaScript
TypeScript 类的基础:从定义到实例化,让你快速掌握(三)
TypeScript 类的基础:从定义到实例化,让你快速掌握
|
7月前
|
缓存 JavaScript 前端开发
TypeScript 类的基础:从定义到实例化,让你快速掌握(一)
TypeScript 类的基础:从定义到实例化,让你快速掌握
|
7月前
|
存储 JavaScript
TypeScript 类的基础:从定义到实例化,让你快速掌握(二)
TypeScript 类的基础:从定义到实例化,让你快速掌握
ES6系列笔记-面向对象/继承
ES6系列笔记-面向对象/继承
35 1
ES6学习(十一)—Class 的基本语法和继承
ES6学习(十一)—Class 的基本语法和继承
继承语法详解
继承语法详解
67 0
|
JavaScript
TypeScript 具有可选的静态类型和基于类的面向对象编程,具体应用案例解析
TypeScript 具有可选的静态类型和基于类的面向对象编程,具体应用案例解析
75 0
|
JavaScript 前端开发 程序员
TypeScript--类(class)
TypeScript--类(class)
|
JavaScript
TypeScript 类(class)与修饰符的详细使用
TypeScript 类(class)与修饰符的详细使用
131 0