class
關鍵字是小寫,大括號中的方法不用逗號分隔。
ES5與ES6實現類的區別
ES5函數實現構造函數
function Student(name) {
this.name = name;
}
Student.prototype.hello = function () {
console.log(('Hello, ' + this.name + '!'));
}
var xiaoming = new Student('小明');
xiaoming.hello();
console.log(xiaoming);
ES6 class
關鍵字實現構造函數
class Student {
constructor(name) { //構造函數
this.name = name;
}
hello() { //原型對象上的函數
console.log(('Hello, ' + this.name + '!'));
}
}
var xiaoming = new Student('小明');
xiaoming.hello();
console.log(xiaoming);
class
表達式
下邊這個類的名字是MyClass
,不是Me
,Me
只在Class
內部代表當前類。
const MyClass = class Me {
getClassName() {
return Me.name;
}
}
let inst = new MyClass();
inst.getClassName() // Me
Me.name // ReferenceError: Me is not defined
類內部沒有用到Me
的話可以省略掉:
const MyClass = class {
//...
};
如果不需要多次構造,可以寫成立即執行的class
:
//沒有類名,只有一個實例
let person = new class {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}('張三');
person.sayName();
不存在變量提升
類class
不存在變量提升,必須先定義後使用,是因爲繼承的存在,防止出現繼承時類未定義。
class
的私有屬性和私有方法
class
的Generator
方法
如果某個方法之前加上星號(*
),就表示該方法是一個 Generator
函數。
下面這個例子是定義一個遍歷器接口:
class Foo {
constructor(...args) {
this.args = args;
}
* [Symbol.iterator]() {
for (let arg of this.args) {
yield arg;
}
}
}
for (let x of new Foo('hello', 'world')) {
console.log(x);
}
// hello
// world
Class的靜態方法
表示是類的屬性,不是實例的屬性:
class Foo {
static classMethod() {
return 'hello';
}
}
Foo.classMethod() // 'hello'
var foo = new Foo();
foo.classMethod() //實例訪問不到
// TypeError: foo.classMethod is not a function
靜態方法中的this
指的是這個類而不是實例。
靜態方法可以被子類繼承,也可以在子類中通過super
對象調用。
class Foo {
static classMethod() {
return 'hello';
}
}
class Bar extends Foo {
}
Bar.classMethod() // 'hello'
//或者像下邊這樣
class Foo {
static classMethod() {
return 'hello';
}
}
class Bar extends Foo {
static classMethod() {
return super.classMethod() + ', too';
}
}
Bar.classMethod() // "hello, too"
class
的靜態屬性和實例屬性
new.target
屬性
可以判斷類是通過new
調用還是通過繼承調用。
需要注意的是,子類繼承父類時,new.target
會返回子類。
不是通過new
調用的話返回undefined
。
class A {
constructor() {
console.log(new.target);
}
}
new A(); //返回A的引用
class B extends A {
}
new B(); //返回B的引用
class
繼承
子類必須在constructor
方法中調用super
方法,否則新建實例時會報錯。這是因爲子類沒有自己的this
對象,而是繼承父類的this
對象,然後對其進行加工。如果不調用super
方法,子類就得不到this
對象。
正因爲如此,在子類的構造函數中,只有調用super
之後,纔可以使用this
關鍵字,否則會報錯。
class PrimaryStudent extends Student { //用extends實現繼承
constructor(name, grade) {
super(name); // 記得用super調用父類的構造方法!
this.grade = grade;
}
myGrade() {
console.log('I am at grade ' + this.grade);
}
}
var xiaoming = new PrimaryStudent("小明", "三年級");
xiaoming.myGrade();
console.log(xiaoming);
super
關鍵字
super
這個關鍵字,既可以當作函數使用,也可以當作對象使用。使用super
的時候,必須顯式指定是作爲函數、還是作爲對象使用,否則會報錯。
作爲函數時,super()
只能用在子類的構造函數之中,代表父類的構造函數,用在其他地方就會報錯。
super
作爲對象時,在普通方法中,指向父類的原型對象;在靜態方法中,指向父類。
作爲父類構造函數
子類構造函數必須調用父類構造函數後才能使用this
。
作爲父類原型對象
super
指向父類的原型對象,所以定義在父類實例上的方法或屬性,是無法通過super
調用的。
class A {
constructor() {
this.a = 1;
}
p() {
return 2;
}
}
class B extends A {
constructor() {
super();
console.log(super.p); // 2
console.log(super.a); // undefined
}
}
let b = new B();
super 與 this
通過super
調用父類的方法時,方法內部的this
指向子類。
就是說通過super
調用父類原型屬性的方法,方法中如果有使用this
,它是指向子類實例的。
class A {
constructor() {
this.x = 1;
}
print() {
console.log(this.x);
}
}
class B extends A {
constructor() {
super();
this.x = 2;
}
m() {
super.print();
}
}
let b = new B();
b.m() // 2
上邊通過super
調用的方法返回了子類實例的屬性。
類似的,如果使用super
對某個屬性賦值,則會賦值到子類實例的屬性上。因爲寫屬性的時候相當於this.PropertyName = ""
。
class A {
constructor() {
this.x = 1;
}
}
class B extends A {
constructor() {
super();
this.x = 2;
super.x = 3;
//super指向父類原型對象,x屬性不存在
console.log(super.x); // undefined
console.log(this.x); // 3
}
}
let b = new B();
作爲父類
super
作爲對象,用在靜態方法之中,這時super
將指向父類,而不是父類的原型對象。
類的原型鏈
Class
作爲構造函數的語法糖,同時有prototype
屬性和__proto__
屬性,因此同時存在兩條繼承鏈。
- 一條是自己作爲對象對類的靜態屬性的繼承,指向父類。
- 另一條是子類的原型對象,對方法的繼承,指向父類的
prototype
屬性。
class A {
}
class B extends A {
}
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
extends的繼承目標
只要是一個有prototype
屬性的函數,就能被繼承。由於函數都有prototype
屬性,所以任意函數都能被繼承。
繼承原生構造函數
Mixin 模式實現
多繼承