ES6中的类是function的语法糖,在编译时转换成了function。虽然class是ES6语法原生实现的,但我们仍可以看看从class到function到底发生了什么。
class:原生实现的function语法糖
一个标准的class由两部分构成:构造器和类成员。class要求显式地声明类成员。
构造器construtor方法做的是创建实例时的工作,比如初始化实例属性、实现单例模式等。构造器内的this指向的是实例。
var classDemo = class {
constructor(params) {
//初始化实例属性instanceMemberOne,this指向实例
this.instanceMemberOne = params;
}
};
类成员分为两种:实例成员和静态成员。实例成员分为实例属性和实例方法,静态成员分为静态属性和静态方法。属性和方法的实现方式是不同的,稍后会讲。
一个包含各类成员的class如下:
var classDemo = class {
constructor(params) {
this.instanceMemberOne = params;
console.log(params)
}
//实例属性
instanceMemberTwo = 100;
//实例方法
instanceFunction(param) {
console.log("this is input :" + param) ;
}
//静态属性
static classMember = 200;
//静态方法
static classMemberFunction(param) {
console.log("this is static function :" + param);
}
//未初始化的实例属性
instanceMember_undefined;
//未初始化的静态属性
static classMember_undefined;
};
编译成function后,类成员放在了三个地方 : function内,function属性、function.prototype属性。
- function内:构造器和实例属性
function classDemo(params) {
(0, _classCallCheck3.default)(this, classDemo); //类检查
this.instanceMemberTwo = 100; //实例属性
this.instanceMemberOne = params; //构造器
console.log(params)
}
- function属性上:静态属性和静态方法
classDemo.classMember === 200 //true
typeof classDemo.classMemberFunction === "function" //true
- function.prototype属性上:实例方法
typeof classDemo.prototype.instanceFunction === "function" //true
而类成员中所有未初始化的实例属性和静态属性都不会被编译。
虽然class是function的语法糖,但class与function不同的是: function内可以写任意语句,而class内只能是声明语句。
class classDemo {
if(condition) {}
else{} //error
}
class内的声明语句还有以下要求:
- 不能有关键字。
- 声明时必须初始化,否则不会编译
- 声明语句之间不能有分号
class classDemo {
var memberOne = "HelloWorld" //error
memberTwo //will not be compiled
memberThree = "HelloWorld"; //error
}
class继承的本质:原型继承
子类的构造器里必须调用super()执行父类的构造器,调用super必须在this之前。
成员方法里可以通过super访问父类,不同的是,实例方法中super指向fatherFunction.prototype,静态方法中super指向fatherFunction
class classDemo {
constructor(params) {
this.instanceMemberOne = params;
console.log(params);
}
//实例方法
instanceFunction(param) {
console.log("this is input :" + param);
}
//静态方法
static classMemberFunction(param) {
console.log("this is static function :" + param);
}
};
class classDemoChild extends classDemo {
constructor() {
super();
}
//实例方法中super指向父函数的原型
childInstanceFunction(param){
super.instanceFunction(param) //console.log("this is input :" + param);
}
//静态方法中super指向父函数
static childClassMemberFunction(param){
super.classMemberFunction(param) //console.log("this is static function :" + param);
}
}
class的继承本质上是原型链的继承,即子函数的原型是父函数的实例
classDemoChild.prototype.__proto__ .constructor === classDemo //true
classDemoChild.prototype.__proto__ === classDemo.prototype //true
classDemoChild.prototype instanceof === classDemo //true
class设计模式:单例模式和装饰者模式
单例模式
Javascript单例模式的实现和其他语言一样,在类中存放已创建的实例,每次new时如果实例已存在,则返回该实例,如果不存在则创建新实例。
class demo{
constructor(params){
if(demo.singleInstance){
return demo.singleInstance
}
else{
this.instanceMember = params
demo.singleInstance = this //编译后不存在constructor方法,this指向实例
}
}
}
装饰者模式
Javascript装饰者模式在不改变原有类结构的基础上,通过function在类声明时修改类结构。
装饰者分为两种:类装饰和成员装饰。类装饰用于修改类的静态成员,成员装饰用于修改类的实例成员的特性(attribute)
@decoratorClass
class demo{
}
//参数target指向demo
function decoratorClass(target){
target.str = "HelloWorld";
}
console.log(demo.str) // Hello world
装饰者模式的注解是一个参入target的function,当然也可以是高阶函数。
@decoratorClass('str')
class demo{
}
//参数target指向demo
function decoratorClass(memberName){
return function(target){
target[memberName] = "HelloWorld";
}
}
console.log(demo.str) // Hello world
成员装饰用于修改实例成员,比类装饰强大的是,成员装饰还可以修改实例成员的特性。
class demo{
@decoratorInstance
sum(a,b){
return a + b;
}
}
//target指向demo.protortpe
//name默认是装饰的方法名,即"sum"
//decorator必须要返回
function decoratorInstance(target,name,decorator){
decorator.value = function(a,b){
return a + b + 10;
}
decorator.writable = false
return decorator
}
var demoInstance = new demo()
console.log(demoInstance.sum(1,2)) // 13
成员装饰的效果和Object.defineProperty()一样
Object.defineProperty(demo.prototype,"sum",{
value:function(a,b){
return a + b + 10;
},
writable:false
})
@decoratorFunction注解语法需要babel的支持,使用前安装babel-plugin-transform-decorators-legacy,并在.babelrc文件里添加配置
{
"plugins":["transform-decorators-legacy"]
}
由于JavaScript动态语言的特性,修改类结构变得很容易,因此实际使用时装饰者模式可以用于提取公共的装饰者逻辑。
总结
ES6在原生语法中加入了class,给JavaScript添加了类特性。TypeScript给JavaScript添加了静态检查,进一步完善了类特性。近来TS越来越热门,Vue3.0的更新也开始更好的支持TS。种种现象表明,虽然Javascript本质上未发生变化,但正向着Java等面向对象语言靠拢发展。
因此,未来对JavaScript的类特性、静态类型等特性上投入学习还是有必要的。