class
class,ES6引入的一個關鍵字,其用法基本上等同於ES5中的原型的繼承,所以class關鍵字的引入並沒有爲JavaScript添加新的繼承模型,而是僅僅作爲一個語法糖被使用,因此JS中的class和其他面嚮對象語言中的class是兩碼事;
因此,在學習class之前,如果能先了解prototype和繼承的運行機制,這樣對class的學習將事倍功效;
先看一個簡單的對比
//ES5的prototype
function ES5(a,b){
this.a = a;
this.b = b;
}
ES5.prototype = {
func:function(){
return this.a + this.b
}
}
//ES6的
class ES6{
constructor(a,b){
this.a = a;
this.b = b;
}
//es6增強字面量寫法
func(){
return this.a + this.b
}
}
如果你瞭解過構造函數,prototype等,那麼相信你很快就可以通過對比,基本瞭解class的使用,發現其效果與構造函數大同小異,比如在ES5的用法中對參數的初始化等同於在class中的constructor函數的使用,而在prototype上定義的方法等同於在class內直接定義函數
下面正式記錄class的用法:
創建
類的創建和函數的創建一樣,都是通過關鍵字創建,函數中使用的是關鍵字function,類則是通過關鍵字class,同樣,類的聲明方式也有兩種:類聲明和類表達式
類聲明
//類聲明
class ES6{
constructor(){}
}
其中,類聲明和函數聲明不同,函數聲明會提升,但類聲明不會提升,如果在聲明前使用類,那麼會拋出一個錯誤;
let p = new Person();//報錯
class Person{}
類表達式
類表達式分爲:具名類表達式和匿名類表達式,兩者的區別在於類的內部有一個默認屬性:name,然後其值不同;
//具名類表達式
const ES6 = class ES6_1{
constructor(){}
}
console.log(ES6.name) //ES6_1
//匿名
const ES6 = class{
constructor(){}
}
console.log(ES6.name) //ES6
驗證
另外,如果你對類ES6進行類型驗證
console.log(typeof ES6) //function
發現,其實這個ES6的類型是function類型,也從側面反應了其實是ES5構造函數那一套東西,既然是function類型,那麼在js中,所有function類型都有一個prototype屬性,如果要給prototype擴展方法,如下例,是不是就是給類擴展方法?
class ES6{}
Object.assign(ES6.prototype,{
func1(){},
func2(){}
})
事實確實可以,另外通過調用類ES6上的方法也可以判斷ES6的類型,假設現在有這樣一個類
class Animal {
constructor() { }
speak() {
console.log(1);
}
}
如果Animal是對象,那麼調用Animal.speak(),應該是可以執行的,但經過測試,不可以,調用speak方法必須通過Animal.prototype.speak()
Animal.speak(); //報錯
Animal.prototype.speak(); //1
從這個例子也可以看出,class其實就是原型對象的用法,它只是將定義在prototype上的方法,直接寫在了class內
constructor方法
在class中的constructor是一個特殊的方法,這種方法用於創建和初始化一個由class創建的對象,一個類只能擁有一個constructor方法;
class ES6{
constructor(a,b){
this.a = a;
this.b = b;
}
}
//等同於
function ES6(a,b){
this.a = a;
this.b = b;
}
extends和super
extend,是一個關鍵字,作用是爲當前的類設置一個父類,設置後,可以從父類那麼繼承到父類上的方法;
class Animal {
constructor(name) { this.name = name; }
speak() {
console.log(this.name);
}
}
// 創建DOg父類
class Dog extends Animal {
speak() {
console.log(`這是${this.name}`);
}
}
let dog = new Dog("旺財");
console.log(dog.speak()) //旺財
我學到這裏的時候,會有個疑問,子類Dog中並沒有設置name屬性的地方,爲什麼dog.speak()會輸出旺財,經過實驗發現,子類中沒有定義自己的constructor,因此會直接使用父類中的contructor,給dog添加了一個name屬性,具體等了解了super再一起分析
super,也是一個關鍵字,作用是調用父級對象上的函數,一共有兩種用法,如下:
super(arguments) //必須寫在contructor中
super.function(arguments)
**
super(arguments),這個用法必須寫在子類的contructor中,作用是調用父類的constructor,比如
class Animal {
constructor(name) { this.name = name; }
speak() {
console.log(this.name);
}
}
// 創建DOg父類
class Dog extends Animal {
constructor(name) {
super(name); //會調用父類上的contructor方法,並將name作爲其參數
}
speak() {
console.log(`這是${this.name}`);
}
}
如果子類有contructor方法,那麼必須在方法內寫上super(),參數可以根據實際情況寫或不寫;另外如果寫了super(),則必須在後續內容的前面,否則會報錯,例如:
class Animal {
constructor(name) { this.name = name; }
speak() {
console.log(this.name);
}
}
// 創建DOg父類
class Dog extends Animal {
constructor(name) {
this.name = name; //報錯,supe()必須寫在最前面
super(name);
}
speak() {
console.log(`這是${this.name}`);
}
}
super.function(arguments),就是調用父類上的方法,這種寫法沒有具體限制,不需要一定寫在contructor裏面
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this)
console.log(this.name);
}
}
// 創建DOg父類
class Dog extends Animal {
constructor(name) {
super(name); // 調用超類構造函數並傳入name參數
}
speak() {
super.speak();
}
}
let dog = new Dog("旺財");
dog.speak() //new的實例化之後會返回一個對象,該對象中的this指向Dog,
爲了更好的理解super,看一個經典示例
class Animal {
constructor(age) {
this.age = 10;
}
}
class Dog extends Animal {
constructor() {
super(); // 調用超類構造函數並傳入name參數
console.log(this.age); //此時Dog並沒有age屬性,因此沿着原型鏈查到父類上面的屬性,返回10
this.age = 2; //設置後,此時的Dog上有個age屬性,並且設置值爲2
console.log(this.age); //這時候Dog上有age了,因此值是2
//根據規範,沒有爲什麼,就是規範,在子類中使用super對某個值進行賦值操作的時候等同與this
//也就是super.age = 18; 等同於 this.age = 18;
super.age = 18;
//而super.age的值,等同與Animal.prototype.age,很明顯Animal的原型上沒有age屬性
//this.age自然就是上面super.age的值
console.log(super.age,this.age); //undefined,18
}
}
let dog = new Dog();
static
static,是一個關鍵字,這個關鍵字使用在類內的方法前面,比如
class ES6{
constructor(a,b){
this.a = a;
this.b = b;
}
static sum(){
return this.a + this.b
}
}
使用static定義的方法稱爲靜態方法,靜態方法代表如果該類被實例化,那麼靜態方法不能被實例對象繼承,但是如果是類之間的繼承則是可以繼承到的,比如實例化上例中的ES6方法
const _es6 = new ES6();
_es6.sum() //報錯,找不到該方法
如果是通過extends繼承,那麼static靜態方法將可以被子類繼承
class Animal {
constructor(name) {
this.name = name;
}
static out(){
return 10
}
speak() {
console.log(this)
}
}
// 創建DOg父類
class Dog extends Animal {
constructor(name) {
super(name); // 調用超類構造函數並傳入name參數
}
}
console.log(Dog.out()) //10
通過例子可以確認,static靜態方法可以在類之間繼承,但是一旦類被實例化,那麼實例化對象將無法繼承static靜態方法;