JavaScript中的Class
- 类的根本语法
- 类的继承
- 润饰器
1. 类的根本语法
能够看成ES5中构造函数的语法糖,它的大部分性能ES5都能够做到
1.1 定义一个类
ES5
function Student(name,age){ this.name = name this.age = age}Student.prototype = { constructor:Student, showName(){ return this.name }}
Class
class Student { constructor(name, age) { this.name = name this.age = age } showName() { return this.name }}console.log(Student.prototype);//{constuctor showName}
class的办法constructor与showName都是定义在原型上的,每个办法之间不必逗号隔开
1.2 constructor办法
class默认的办法,没有定义就默认加一个空的办法,在用new
生成实例时主动调用该办法
class Student { constructor(name, age) { this.name = name this.age = age } showName() { return this.name }}const std1 = new Student("张三")console.log(std1);//{name:"张三",age:undefined}
与ES5不同的是,定义类不存在变量晋升,就是实例化肯定要在定义之后
1.3 公有办法和公有属性
只在类的外部调用和应用的办法与属性
公有办法
const private1 = Symbol('private1')const prop1 = Symbol('prop1')class Studnet { //私有办法 foo(parm1, parm2) { //外部应用公有办法 this[private1](parm1) private2.call(this, parm2) } //公有办法 [private1](parm) { return this[prop1] = parm }}//公有办法function private2(parm) { return this.prop2 = parm}
应用Symbol或者内部的构造函数的形式,能够实现办法外部的公有,实例化的对象不能拜访到这些办法
公有属性
class Studnet { #a = 'private' constructor(a) { this.a = this.#a } get() { return this.#a }}
用#来标识公有属性,在内部不能间接拜访这个属性,仅供外部应用
1.4 静态方法和动态属性
不能被实例继承的办法与属性
静态方法
class Foo { static fn1() { this.fn2() } static fn2() { console.log("Hello"); } fn2() { console.log("World"); }}Foo.fn1() //"Hello"let foo = new Foo()foo.fn2() //"World"
静态方法能够和实例办法重名,静态方法只能在类上调用
class Foo1 extends Foo {}Foo1.fn2()// "Hello"
子类能够继承父类的静态方法
动态属性
与静态方法雷同,在实例属性前加static即可
class Foo { static _name = "a" _name = "b"}console.log(Foo._name);//"a"let foo = new Foo()console.log(foo._name);//"b"
静态方法能够与实例办法重名,实例不能继承到静态方法,它属于结构器,不属于原型对象
同样静态方法能够被子类继承
1.5 new.target属性
在类中指向该类,父类被继承时指向子类
class Foo { constructor() { console.log(new.target); }}class Foo1 extends Foo { }let foo = new Foo() //Foolet foo1 = new Foo1()//Foo1
用该办法能够管制一些类必须在继承后能力应用
class Foo { constructor() { if (new.target === Foo) { throw new Error("须要先继承") } else { console.log(new.target); } }}class Foo1 extends Foo { }let foo = new Foo() //Error 须要先继承let foo1 = new Foo1() //Foo1
2 Class的继承
应用extends关键字将父类中的属性与办法拷贝到子类
2.1 super关键字
- 类继承的本质是先创立父类的实例对象this,再用子类的构造函数批改this
super就为子类提供了父类的this对象,相当于Parent.prototype.constructor.call(this),执行父类构造函数并批改this
class Parent { constructor(x, y) { this.x = x this.y = y } getX() { console.log(this.x); }}class Child extends Parent { constructor(x, y, z) { super(x, y) this.z = z } getY() { console.log(this.y); }}let obj1 = new Child(1, 2, 3)console.log(obj1);//{x:1,y:2,z:3}obj1.getX()//1obj1.getY()//2
super指向父类的原型,能够作为一个对象去调用原型上的办法,并批改this指向子类
class Parent { constructor(x) { this.x = x this.y = "a" } getY() { console.log(this.y); }}class Child extends Parent { constructor(x, y) { super(x) this.y = y } runner() { super.getY() }}let obj1 = new Child(1, 2)obj1.runner()//2
super.getY()相当于Parent.prototype.getY.call(this)
如果Child上没有y属性,则返回父类的y值为"a"
如果Child上有y属性,但实例化的时候没有传值,则返回undefined,不会去原型链上找
2.2 原型链
class A { }class B extends A { }const a1 = new A()const b1 = new B()//子类的__proto__指向父类B.__proto__ === A//true//父类的原型对象是子类原型对象的原型B.prototype.__proto__ === A.prototype//true//父类实例的原型是子类实例的原型的原型就b1.__proto__.__proto__ === a1.__proto__
要害还是在于第二个示例,父类的原型对象是子类原型对象的原型
因为类的原型对象就是实例对象的__proto__指向的对象,能够推出第三个示例
2.3混入(Minxin)
实现继承多个类,先将多个类混入到一个类中,再继承
Minxin的实现
//混入function mix(...mixins) { class Mix { } for (let mixin of mixins) { copyProperties(Max, mixin) copyProperties(Max.prototype, mixin.prototype) } return Mix}//拷贝属性function copyProperties(target, source) { for (let key of Reflect.ownKeys(source)) { if (key !== "constructor" && ket !== "prototype" && key !== "name" ) { let desc = Object.getOwnPropertyDescriptor(source, key); Object.defineProperty(target, key, desc) } }}
ES6规范 阮一峰
如果只是办法的追加则应用Object.assign(target,source)
即可
3 润饰器(Decorator)
润饰器能够用来润饰类的行为
润饰器还在提案中,然而babel转码器曾经反对润饰器
能够装置babel的模块去编译 应用了润饰器的写法,转码成向下兼容的ES5写法,这样就能够失去想要的后果
这里仅举一个类润饰器的例子
@addStaticclass A{}function addStatic(target){ target.hello = "Hello"}
target就是类A,@ + 润饰函数 相当于给类A增加一个动态属性hello
属性润饰器以及其余内容不再举例