Class
类彻底能够看做构造函数的另外一种写法,类的数据类型其实就是函数,类自己也就是其实例的构造函数。使用的时候也是使用 new
命令。javascript
class Person { constructor(name,age) { this.name = name; this.age = age; } getName(){ console.log(`My name is ${this.name}!`) } static sayHi() { console.log('Hi'); } } let p = new Person(); typeof Person // "function" p instanceof Person // true
constructor
方法就是类的构造方法,this
关键字表明实例对象。其对应的也就是 ES5 的构造函数 Person
。java
constructor
方法是类的默认方法,经过 new
命令生成对象实例时,会自动调用该方法。一个类必须有 constructor
方法,若是没有显式定义,会默认添加一个空的 constructor
方法。函数
class Person {} Person.prototype // {construtor:f}
constructor
方法默认返回实例对象,也彻底能够 return
另一个对象。this
class Foo { constructor(){ return Object.create(null); } } new Foo() instanceof Foo // false
类必须使用 new
调用,也就是 constructor
方法只能经过 new
命令执行,不然会报错。prototype
class Foo { constructor(){} } Foo() // TypeError: Class constructor Foo cannot be invoked without 'new'
Class
类依旧存在 prototype
属性,且类的全部方法都定义在 prototype
属性上面。code
class Person { constructor() {} aaa(){} bbb(){} } Object.getOwnPropertyNames(Person.prototype) // ["constructor", "aaa", "bbb"]
prototype
对象的 constructor
属性,也是直接指向类自己。对象
Person.prototype.constructor === Person // true p.constructor === Person // true
类的新方法可经过 Object.assign
向 prototype
一次性添加多个。继承
class Person { constructor() {} } Object.assign(Person.prototype, { aaa(){}, bbb(){} })
注意,定义类的时候,前面不须要加上 function
关键字,也不须要逗号分隔。ip
与 ES5 构造函数的不一样的是,Class
内部全部定义的方法,都是不可枚举的。ci
class Person { constructor() {} aaa(){} } Object.keys(Person.prototype) // [] Object.getOwnPropertyNames(Person.prototype) // ["constructor", "aaa"]
而 ES5 的构造函数的 prototype
原型定义的方法是可枚举的。
let Person = function(){}; Person.prototype.aaa = function(){}; Object.keys(Person.prototype) // ["aaa"] Object.getOwnPropertyNames(Person.prototype) // ["constructor", "aaa"]
Class 类的属性名,能够采用表达式。
let methodName = 'getName'; class Person { constructor() {} [methodName](){} } Object.getOwnPropertyNames(Person.prototype) // ["constructor", "getName"]
与 ES5 同样,在 Class
内部可使用 get
和 set
关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。
class Person { constructor() { this.name = 'dora'; } get author() { return this.name; } set author(value) { this.name = this.name + value; console.log(this.name); } } let p = new Person(); p.author // dora p.author = 666; // dora666
且其中 author
属性定义在 Person.prototype
上,但 get
和 set
函数是设置在 author
属性描述对象 Descriptor
上的。
Object.getOwnPropertyNames(Person.prototype) // ["constructor", "author"] Object.getOwnPropertyDescriptor(Person.prototype,'author') // { get: ƒ author()(), // set: ƒ author()(value), // ... // }
类至关于实例的原型,全部在类中定义的方法,都会被实例继承。但若是在一个方法前,加上 static
关键字,就表示该方法不会被实例继承,而是直接经过类来调用,这就称为“静态方法”。
class Person { static sayHi() { console.log('Hi'); } } Person.sayHi() // "Hi" let p = new Person(); p.sayHi() // TypeError: p.sayHi is not a function
若是静态方法包含 this
关键字,这个 this
指的是类,而不是实例。静态方法能够与非静态方法重名。
class Person { static sayHi() { this.hi(); } static hi(){ console.log('hello') } hi(){ console.log('world') } } Person.sayHi() // "hello"
实例属性除了定义在 constructor()
方法里面的 this
上面,也能够定义在类的最顶层。此时定义的时候,属性前面不须要加上 this
。而在类内部其它地方调用的时候,须要加上 this
。
class Person { name = 'dora'; getName() { return this.name; } } let p = new Person(); p.name // "dora" Object.keys(p) // ["name"]
这种写法的好处是,全部实例对象自身的属性都定义在类的头部,看上去比较整齐,写法简洁,一眼就能看出这个类有哪些实例属性。
Class
子类能够经过 extends
关键字实现继承。
class Person { constructor() {} sayHi() { return 'Hi'; } } class Teacher extends Person { constructor() { super(); } } let t = new Teacher(); t.sayHi(); // "Hi"
子类必须在 constructor
方法中调用 super()
方法,不然新建实例时会报错。
若是子类没有定义 constructor
方法,这个方法会被默认添加,且子类默认添加的 constructor
方法都会默认执行 super()
方法。
class Teacher extends Person { } let t = new Teacher(); t.sayHi(); // "Hi"
等同于
class Teacher extends Person { constructor(...args) { super(...args); } }
super
这个关键字,既能够看成函数使用,也能够看成对象使用。用法彻底不一样。
super
做为函数调用时,表明父类的构造函数。子类的构造函数必须执行一次 super()
方法。
由于 ES6 的继承机制与 ES5 构造函数不一样,ES6 的子类实例对象 this
必须先经过父类的构造函数建立,获得与父类一样的实例属性和方法后再添加子类本身的实例属性和方法。所以若是不调用 super()
方法,子类就得不到 this
对象。
super
虽然表明了父类的构造函数,但返回的是子类的实例,即经过super
执行父类构造函数时,this
指的都是子类的实例。也就是 super()
至关于 Person.call(this)
。
class A { constructor() { console.log(this.constructor.name) } } class B extends A { constructor() { super(); } } new A() // A new B() // B
做为函数时,super()
只能在子类的构造函数之中,用在其余地方就会报错。
在普通方法中指向父类的 prototype
原型
super
做为对象时,在普通方法中,指向父类的 prototype
原型,所以不在原型 prototype
上的属性和方法不能够经过 super
调用。
class A { constructor() { this.a = 3; } p() {return 2;} } A.prototype.m = 6; class B extends A { constructor() { super(); console.log(super.a); // undefined console.log(super.p()); // 2 console.log(super.m); // 6 } } new B();
在子类普通方法中经过 super
调用父类方法时,方法内部的 this
指向当前的子类实例。
class A { constructor() { this.x = 'a'; } aX() { console.log(this.x); } } class B extends A { constructor() { super(); this.x = 'b'; } bX() { super.aX(); } } (new B()).bX() // 'b'
在静态方法中,指向父类
class A { static m(msg) { console.log('static', msg); } m(msg) { console.log('instance', msg); } } class B extends A { static m(msg) { super.m(msg); } m(msg) { super.m(msg); } } B.m(1); // "static" 1 (new B()).m(2) // "instance" 2
在子类静态方法中经过 super
调用父类方法时,方法内部的 this
指向当前的子类,而不是子类的实例。
因为对象老是继承其它对象的,因此能够在任意一个对象中,使用 super
关键字,指向的是该对象的构造函数的 prototype
原型。
let obj = { m() { return super.constructor.name; } }; obj.m(); // Object
注意,使用 super
的时候,必须显式的指定是做为函数仍是做为对象使用,不然会报错。
class B extends A { m() { console.log(super); } } // SyntaxError: 'super' keyword unexpected here
父类的静态方法,能够被子类继承。
class Person { static sayHi() { return 'hello'; } } class Teacher extends Person { } Teacher.sayHi() // "hello"
在子类的 static
内部,能够从 super
对象上调用父类的静态方法。
class Teacher extends Person { static sayHi() { super.sayHi(); } } Teacher.sayHi() // "hello"
Class
内部调用 new.target
,返回当前 Class
。且子类继承父类时,new.target
会返回子类。所以利用这个特色,能够写出不能独立使用必须继承后才能使用的类。
class Shape { constructor() { if(new.target === Shape) { throw new Error('本类不能实例化'); } } } class Circle extends Shape { constructor(radius) { super(); console.log('ok'); } } let s = new Shape(); // 报错 let cir = new Circle(6); // 'ok'