js學習筆記之class類、super和extends關鍵詞
前言
JavaScript 語言在ES6中引入瞭 class 這一個關鍵字,在學習面試的中,經常會遇到面試官問到談一下你對 ES6 中class的認識,同時我們的代碼中如何去使用這個關鍵字,使用這個關鍵字需要註意什麼,這篇來總結一下相關知識點。
正文
1.es6之前創建對象
先來看下es6之前我們要想創建一個對象,隻能通過構造函數的方式來創建,將靜態方法添加在原型上面使得每一個實例能夠調用該方法。
function Person(name, age) { this.name = name this.age = age Person.prototype.sayHello = function () { return "hello," + this.name + ",早上好" } } let person = new Person("serendipity", 18) console.log(person.sayHello())//hello,serendipity,早上好 console.log(person instanceof Person);//true console.log(person instanceof Object);//true
2.es6之後class的聲明
類是用於創建對象的模板,他們用代碼封裝數據以處理該數據。js中的 class 類建立在原型之上,但也具有某些語法和語義與ES5類相似語義共享。
實際上,類是一種特殊的函數,就像定義函數聲明和函數表達式一樣,類的語法也有兩個部分組成:類聲明和類表達式。
class Person { constructor(name, age) {//自有屬性,該屬性出現在實例上,隻能在類的構造器或者方法內部進行創建 this.name = name this.age = age } sayHello() {//等價於Perosn.prototype.sayHello return `hello,${this.name},早上好` } } let person = new Person("serendipity", 18) console.log(person.sayHello());//hello,serendipity,早上好 console.log(person instanceof Person);//true console.log(person instanceof Object);//true console.log(typeof Person);//function console.log(typeof Person.prototype.sayHello);//function
類聲明允許在class中使用 constructor 方法定義一個構造器,而不需要定義專門的構造方法來當構造器使用。
class 類的語法與普通es5之前的函數語法相似,但是還存在一些特性需要註意:
(1)類的聲明不會被提升,類的聲明行為和 let 相似,因此執行時類會存在暫時性死區;
(2)類中所有代碼自動運行在嚴格模式下,且改嚴格模式無法退出
(3) 類中所有方法都是不可枚舉的,普通自定義方法隻有通過 object.defineProperty() 才能將方法定義為不可枚舉
(4)類中的所有方法內部都沒有 [[construct]] ,因此使用new 來調用他們會拋出錯誤
(5)調用類構造器時不使用 new 會拋出錯誤
(6)試圖在類的方法內部重寫類名會拋出錯誤
將上面的代碼轉換為ES5之前的寫法如下:
let PersonClass = (function () { "use strict" const PersonClass = function (name, age) { // 判斷是否被new調用構造函數 if (typeof new.target === "undefined") { throw new Error("Constructor must be call with new.") } this.name = name this.age = age } Object.defineProperty(PersonClass.prototype, "sayHello", { value: function () { if (typeof new.target !== "undefined") {//保正調用時沒有使用new throw new Error("Method cannot be called with new.") } return "hello," + this.name + ",早上好!" }, enumerable: false, configurable: true, writable: true }) return PersonClass })() var personClass = new PersonClass("serendipity", 18) console.log(personClass.name);//serendipity console.log(personClass.sayHello());///hello,serendipity,早上好!
兩個PersonClass 聲明,一個在外部作用域的 let 聲明,另一個在立即執行函數內部的 const 聲明,這就是為何類的方法不能對類名進行重寫,而類的外部的代碼則被允許。同時,隻在類的內部類名才被視為使用瞭const聲明,這意味著你可以在外部(相當於let)重寫類名,但是不能在類的方法內部這麼寫。
3.類的繼承
ES6之前的繼承方式主要通過構造函數和原型鏈組合的方式來實現繼承,具體代碼如下:
function Rectangle(length, width) { this.length = length this.width = width Rectangle.prototype.getArea = function () { return this.length * this.width } } function Square(length) { Rectangle.call(this, length, length) } Square.prototype = Object.create(Rectangle.prototype, { constructor: { value: Square, enumerble: true, writeable: true, configurable: true } }) var square = new Square(3) console.log(square.getArea());//9 console.log(square instanceof Square);//true console.log(square instanceof Rectangle);//true
上面的代碼通過構造函數和原型上面添加靜態方法實現瞭 Rectangle 父類,然後子類 Square 通過 Rectangle.call(this,length,length) 調用瞭父類的構造函數,Object.create 會在內部創建一個空對象來連接兩個原型對象,再手動將 constructor 指向自身。這種方法實現繼承代碼繁雜且不利用理解,於是ES6 class 類的創建讓繼承變得更加簡單,使用extends 關鍵字來指定當前類所需要繼承的父類,生成的類的原型會自動調整,還可以使用 super() 方法來訪問基類的構造器。具體代碼如下:
class Rectangle { constructor(length, width) { this.length = length this.width = width } getArea() { return this.length * this.width } } class Square extends Rectangle { constructor(length) { super(length, length) } getArea() { return this.length + this.length } } var square = new Square(3) console.log(square.getArea());//6 console.log(square instanceof Square);//true console.log(square instanceof Rectangle);//true
上面的代碼中 Square 類重寫瞭基類的 getArea() 方法,當派生的子類中函數名和基類中函數同名的時候,派生類的方法會屏蔽基類的方法,同時也可以再子類中getArea () { return super.getArea() }中調用基類的方法進行擴展。
4.繼承類的靜態成員
靜態成員:直接在構造器上添加的額外的方法。例如ES5中在原型上添加的方法就屬於靜態成員,ES6 class類引入簡化瞭靜態成員的創建,隻需要在方法與訪問器屬性的名稱前添加 static關鍵字即可。例如下面的代碼用於區分靜態方法和實例方法。
function PersonType(name) { this.name = name; } // 靜態方法 PersonType.create = function(name) { return new PersonType(name); }; // 實例方法 PersonType.prototype.sayName = function() { console.log(this.name); }; var person = PersonType.create("Nicholas");
在ES6中要想使用靜態成員如下:
class Rectangle { constructor(length ,width) { this.length = length this.width = width } getArea() { return this.length * this.width } static create(length,width) { return new Rectangle(length , width) } } class Square extends Rectangle{ constructor (length){ super(length,length) } } var square =Square.create(3,4) console.log(square.getArea());//12 console.log(square instanceof Square);//false console.log(square instanceof Rectangle);//true
上面的代碼中,一個新的靜態方法 create() 被添加到 Rectangle 類中,通過繼承,以Square.create() 的形式存在,並且其行為方式與Rectangle.create() 一樣。需要註意靜態成員不懂通過實例來訪問,始終需要直接調用類自身來訪問他們。
寫在最後
以上就是本文的全部內容,希望給讀者帶來些許的幫助和進步,方便的話點個關註,小白的成長踩坑之路會持續更新一些工作中常見的問題和技術點。
推薦閱讀:
- 通過手寫instanceof理解原型鏈示例詳解
- JavaScript三大重點同步異步與作用域和閉包及原型和原型鏈詳解
- 全面解析js中的原型,原型對象,原型鏈
- JavaScript基礎之函數詳解
- 詳解js創建對象的幾種方式和對象方法