ES6學習筆記(七)【class】

風靈使發表於2019-04-06

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,不是MeMe只在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的私有屬性和私有方法

classGenerator方法

如果某個方法之前加上星號(*),就表示該方法是一個 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 模式實現

多繼承

相關文章