当前位置: 首页 > 后端技术 > Node.js

[ES6]继承类语法更简单

时间:2023-04-03 14:30:31 Node.js

与其他面向对象编程语言一样,ES6正式定义了类class和扩展继承语法糖,并支持static、derivation、abstraction、iteration、singleton等,并根据ES6新功能带来了许多有趣的用法。1、类的基本定义基本上所有的面向对象语言都支持类的封装和继承,那么什么是类呢?类是面向对象编程的基础,包含数据封装、数据操作和消息传递的功能。类的实例称为对象。在ES5之前,通过函数模拟类的实现如下://constructorfunctionPerson(name){this.name=name;}//methodPerson.prototype.sayName=function(){console.log(this.name);};//新建一个实例varfriend=newPerson("Jenny");friend.sayName();//Jennyconsole.log(friendinstanceofPerson);//trueconsole.log(friendinstanceofObject);//true综上所述,定义一个类的思路是这样的:1.需要构造函数来封装数据;2、在原型上添加操作数据的方法;方法名[[Construct]]定义构造函数,[[Construct]]在new创建实例时调用,示例如下:/*ES6*///等价于letPerson=class{classPerson{//constructorconstructor(name){this.name=name;}//相当于Person.prototype.sayNamesayName(){console.log(this.name);}}console.log(typeofPerson);//functionconsole.log(typeofPerson.prototype.sayName);//functionletfriend=newPerson("Jenny");friend.sayName();//Jennyconsole.log(friendinstanceofPerson);//trueconsole.log(friendinstanceofObject);//true在上面的例子中,class定义的类和自定义函数模拟类在功能上看似没有区别,但本质上还是有很大区别的:函数声明可以提升,但是类声明类似于let不能升级;类声明自动以严格模式运行,“使用严格”;类中的所有方法都是不可枚举的,enumerable为false二、更灵活的类类和函数一样,是JavaScript的一等公民(可以通过输入函数,从函数返回,赋值),注意有类和对象文字之间的更多相似之处。这些特性可以更灵活地扩展类的定义和使用。2.1具有访问器属性的对象的属性包括数据属性和访问属性。访问器属性也可以通过类中的get和set关键字来定义:classPerson{constructor(name){this.name=name;}getvalue(){returnthis.name+this.age}setvalue(num){this.age=num}}letfriend=newPerson("Jenny");//callsetterfriend.value=18//call吸气机。log(friend.value)//Jenny182.2可计算成员名称类似于ES6对象字面量扩展的可计算属性名称。类也可以使用[expression]来定义可计算的成员名称,包括类属性中的方法和访问器:letmethodName='sayName'classPerson{constructor(name){this.name=name;}[methodName+'Default'](){console.log(this.name);}get[methodName](){returnthis.name}set[methodName](str){this.name=str}}letfriend=newPerson("Jenny");//方法friend.sayNameDefault();//Jenny//访问器属性朋友。sayName='lee'console.log(friend.sayName)//如果lee想进一步熟悉对象的新特性,请参考:【ES6】对象的新功能和解构赋值2.3定义默认迭代器Collection对象(array,Set)ES6中常用/Mapcollection)和字符串都是可迭代对象,如果用类来表示这些可迭代对象,定义一个默认的迭代器会更有用。ES6通过向Symbol.iterator属性添加生成器来定义默认迭代器:classPerson{constructor(name){this.name=name;}*[Symbol.iterator](){for(letitemofthis.name){yielditem}}}varabbrName=newPerson(newSet(['j','j','e','e','n','y','y','y',]))for(letxofabbrName){console.log(x);//jeny}console.log(...abbrName)//jeny定义了默认迭代器后,类的实例可以使用for-of循环和展开operator(...)等迭代函数。如果你对上面迭代器的内容感到困惑,请参考:[ES6]IteratorsandIterableObjects2.4Classesasparameters作为“一等公民”,它们可以作为参数传入函数,当然它们也可以从函数返回:functioncreateClass(className,val){returnnewclassName(val)}letperson=createClass(Person,'Jenny')console.log(person)//Person{name:'Jenny'}console.log(typeofperson)//object2.5创建单例使用类语法创建单例立即通过new调用类表达式:letsingleton=newclass{constructor(name){this.name=name;}}('Jenny')console.log(singleton.name)//这里Jenny先创建一个匿名类表达式,然后用new调用这个类表达式,通过括号立即执行。通过此类语法创建的单例不会在范围内公开类引用。3.类继承回顾ES6之前如何实现继承?常见的方式有通过原型链、构造函数和组合继承。ES6类使用熟悉的extends关键字指定类继承的功能,可以通过surpe()方法访问父类的构造函数。比如继承一个Person类:classFriendextendsPerson{constructor(name,phone){super(name)this.phone=phone}}letmyfriend=newFriend('lee',2233)console.log(myfriend)//Friend{name:'lee',phone:2233}Friend继承自Person。在术语中,Person称为基类,Friend是派生类。需要注意的是,surpe()只能在派生类中使用,它负责初始化this,所以在派生类中使用this之前必须先使用surpe()。3.1继承内置对象ES6类继承可以继承内置对象(Array、Set、Map等),继承后可以拥有基类的所有内置功能。例如:classMyArrayextendsArray{}letarr=newMyArray(1,2,3,4),subarr=arr.slice(1,3)console.log(arr.length)//4console.log(arrinstanceofMyArray)//trueconsole.log(arrinstanceofArray)//trueconsole.log(subarrinstanceofMyArray)//true注意上面的例子中,不仅arr是派生类MyArray的一个实例,subarr也是一个实例派生类MyArray,和内置对象继承的效用是改变返回对象的类型。浏览器引擎通过[Symbol.species]属性实现此行为,该属性用于返回函数的静态访问器属性。定义[Symbol.species]属性的内置对象包括Array、ArrayBuffer、Set、Map、Promises、RegExp、Typedarrays。3.2继承表达式的类目前extends可以继承类和内置对象,但是它有一个更强大的功能,就是从表达式中导出类!这个表达式要求它可以被解析为一个函数并且具有[[Construct]]属性和一个原型,一个例子如下:functionSup(val){this.value=val}Sup.prototype.getVal=function(){return'hello'+this.value}classDerivedextendsSup{constructor(val){super(val)}}letder=newDerived('world')console.log(der)//Derived{value:'world'}console.log(der.getVal())//helloworld3.3是抽象类,只能继承。ES6引入了new.target元属性来判断函数是否通过new关键字调用。类构造函数还可以确定如何通过new.target调用类。抽象类(不能实例化的类)可以通过new.target创建,例如:classAbstract{constructor(){if(new.target===Abstract){thrownewError('抽象类(不能直接实例化)')}}}classInstantiableextendsAbstract{constructor(){super()}}//letabs=newAbstract()//错误:抽象类(不能直接实例化)letabs=newInstantiable()console.log(absinstanceofAbstract)//trueAbstract抽象类虽然不能直接用来创建实例,但是可以作为基类派生其他类。四、类的静态成员ES6使用static关键字来声明静态成员或方法。静态可以在类的方法或访问器属性之前使用,唯一的限制是它不能在构造函数中使用。静态成员的作用是将某些类成员私有化,不能在实例中访问,必须直接在类上访问。类Person{构造函数(名称){this.name=name;}staticcreate(name){returnnewPerson(name);}}letbeauty=Person.create("Jenny");//beauty.create('lee')//TypeError如果基类有静态成员,那么派生类中也可以使用这些静态成员。比如上面例子中的Person作为基类,派生Friend类,使用基类的静态方法create():classFriendextendsPerson{constructor(name){super(name)}}varfriend=Friend.create('lee')console.log(friendinstanceofPerson)//trueconsole.log(friendinstanceofFriend)//false可见派生类仍然可以使用基类的静态方法。推荐阅读《深入理解ES6》加油吧少年!