es5与es6以及Typescript中类的创建与继承
闲来无事,准备总结一下js中类的创建相关知识。说到js中的类相关知识,自然不能没有原型链相关知识点,所以不懂原型链的小伙伴,建议先去学习一波原型链相关知识点。
es5中创建类与类的继承
创建Person类
//创建Person类构造函数,即以构造函数的方式创建Javascript对象
function Person(name){
this.name = name
this.sayname=function(){
console.log('my name is '+this.name)
}
}
//Person类原型上添加say方法
Person.prototype.say = function(){
console.log('my name is '+this.name)
}
//Person类添加静态方法fnc
Person.fnc = function(){
console.log('我是静态方法,只在构造函数上调用')
}
var aliang = new Person('aliang')
Person.fnc() //我是静态方法,只在构造函数上调用
aliang.say() //my name is aliang
aliang.sayname() //my name is aliang
console.log(aliang instanceof Person) //true
在Person类的创建中我们设置了三个方法原型方法say和实例方法sayname以及一个静态方法fnc,可以看到fnc是只在构造函数上调用的,是访问不到实例的。另外两个方法执行中都能访问到当前实例aliang的name属性,那他们之间到底有区别呢
var person1 = new Person('aming')
var person2 = new Person('ali')
console.log(person1.say === person2.say) //true
console.log(person1.sayname === person2.sayname) //false
从上面代码的执行结果,我们可以看到,构造函数内的方法,在不同实例中是不一样的,也就是其指向的内存中的sayname函数是不一样的。而say方法是一样的,也就是person1和person2上的say方法指向同一个函数。
其实这是因为当我们通过Person创建这两个实例时,在构造函数内的代码分别执行了两次,从而创建了两个不同sayname方法,而say方法是定义在原型对象上,而我们知道当构造函数创建一个实例时,会让实例对象的_proto_指向构造函数的原型对象prototype,所以person1与person2的_proto_指向同一个对象,所以它们的say方法是一样的,只是方法中访问到的this肯定不一样了,所以在开发中我们可以合理利用这三种方法来实现我们的需求。
继承Person类
此处介绍ES5中常用的组合式继承
//创建Person类构造函数,即以构造函数的方式创建Javascript对象
function Person(name){
this.name = name
this.sayname=function(){
console.log('my name is '+this.name)
}
}
//Person类原型上添加say方法
Person.prototype.say = function(){
console.log('my name is '+this.name)
}
Person.fnc = function(){
console.log('我是静态方法,只在构造函数上调用')
}
//创建LIli类
function LIli(name,age){
Person.call(this,name) //执行Person构造函数,使LIli继承到Person内的属性
this.age = age
}
LIli.prototype = new Person() //让LIli的原型对象的_proto_属性指向Person的原型对象,来继承Person的原型对象
LIli.prototype.constructor = LIli //使LIli的原型对象的constructor属性指向自身
LIli.prototype.sayall = function(){
console.log('这是 '+this.name+',他'+this.age+'岁')
}
var lili = new LIli('lili',18)
LIli.fnc() //LIli.fn is not a function
lili.sayall() //这是 lili 他18岁
lili.say() //my name is lili
lili.sayname() //my name is lili
可以看到这种继承方式中,LIli类继承了Person类中的方法与属性,但是没有继承Person的静态方法fnc;
这就是es5中类的创建与继承了,基本都是直接操作原型链来进行的,下面继续介绍ES6中类的创建与继承。
es6中创建类与类的继承
创建Person类
es6中通过class关键字来创建类,extends关键字来继承类,其底层原理与es5一样,也是基于原型链,该实现方法只是一种语法糖。
class Person{
constructor(name) {
this.name = name
this.sayname=function(){
console.log('my name is '+this.name)
}
}
say(){
console.log('my name is '+this.name)
}
static fnc(){
console.log('我是静态方法,只在构造函数上调用')
}
}
var aliang = new Person('aliang')
Person.fnc() //我是静态方法,只在构造函数上调用
aliang.say() //my name is aliang
aliang.sayname() //my name is aliang
console.log(aliang instanceof Person) //true
此处创建了一个与es5中相同的一个Person类,可以看到es5中写在函数中的内容放到了constructor中,原型对象上的方法直接写在类里面,而类上的静态方法是在函数名之前写了一个static关键字。
继承Person类
class LIli extends Person{
constructor(name,age) {
super(name)
this.age = age
}
sayall(){
console.log('这是 '+this.name+',他'+this.age+'岁')
}
}
var lili = new LIli('lili',18)
LIli.fnc() //我是静态方法,只在构造函数上调用
lili.sayall() //这是 lili 他18岁
lili.say() //my name is lili
lili.sayname() //my name is lili
可以看到结果与上面es5的继承结果相差不大,唯一的区别就是fnc方法被LIli类继承下来了,也就是es6中类的继承会将静态方法也继承下来。
而且此处构造函数中多了super(),super函数的作用类似es5继承中调用Person函数一样,super()需要传入创建子类实例时传入的父类构造函数的参数,就算不需要给继承的实例属性传值,也需要在constructor首部调用一次super()否则后面的语句不能使用this,当然如果不需要定义实例属性,可以直接不写constructor函数。
Typescript中创建类与类的继承
创建Person类
class Person{
private sex:string //允许在当前类内部调用
protected height:number //允许在类内及继承的子类中使用
public name:string
public sayname:()=>void;
constructor(name:string){
this.name = name
this.sayname=function(){
console.log('my name is '+this.name)
}
}
//constructor(public name:string){ } //可省略为此种写法
public say(){
console.log('我的名字是'+this.name+'我的技能')
this.play1()
this.play2()
}
private play1(){ //允许在当前类内部调用
console.log('打篮球')
}
protected play2(){ //允许在类内及继承的子类中使用
console.log('踢足球')
}
static fnc(){
console.log('我是静态方法,只在构造函数上调用')
}
}
var aliang = new Person('aliang')
Person.fnc() //我是静态方法,只在构造函数上调用
aliang.play1() //编译报错 Property 'play1' is private and only accessible within class 'Person'
aliang.say() //我的名字是aliang我的技能 打篮球 踢足球
aliang.sayname() //my name is aliang
console.log(aliang instanceof Person) //true
这样我们就在TS中创建了一个Person类,可以看到这里多了很多es6中没有的修饰词,用来定义访问类型public,private,protected,写在类中的无论属性还是方法,没有加这三种修饰词的都是默认public,private指该属性或方法只能在当前类中使用,protected指该属性或方法只能在当前类中与其子类中使用;
此处需要注意的是,与ES6不同,在TS构造函数constructor中使用this访问的属性必须要先在类顶部定义,或者在构造函数的参数中定义,否则也会导致报错。
继承Person类
class LIli extends Person{
constructor(name:string,public age:number,private _sex:string){
super(name)
}
public sayall(){
console.log('这是 '+this.name+',他'+this.age+'岁,他的性别是'+this._sex)
this.play2()
}
}
let aming = new LIli('lili',100,'男')
aming.sayall() //这是 aming,他100岁,他的性别是男; 踢足球
aming.play2() //编译报错 Property 'play2' is protected and only accessible within class 'Person' and its subclasses.
console.log(aming._sex) //编译报错 Property '_sex' is private and only accessible within class 'Man'
这样就使LIli类继承了Person类,从运行结果可以看到,private定义的属性,只能在类里面使用,不可以在实例上使用,父类中protected定义的方法可以在子类中调用,不可以在子类实例上调用。
而且此处constructor函数我们也是使用了相比于定义Person类时更简洁的写法,也是比较常用的写法。
这样关于es5与es6以及Typescript中类的创建与继承总结就结束了,其实在js中,不管用什么写法,他的底层都是不变的,都是基于原型链来进行的,所以弄懂Javascript原型链很重要。
第一次写,如果有不对的地方,请指出,多谢【抱拳】