主頁 > 企業開發 > JavaScript:類(class)

JavaScript:類(class)

2022-12-25 07:12:37 企業開發

在JS中,類是后來才出的概念,早期創造物件的方式是new Function()呼叫建構式創建函式物件;

而現在,可以使用new className()構造方法來創建類物件了;

所以在很多方面,類的使用方式,很像函式的使用方式:

但是類跟函式,還是有本質區別的,這在原型那里已經說過,不再贅述;

如何定義一個類

如下所示去定義一個類:

class className {
    // 屬性properties
    property1 = 1;
    property2 = [];
    peoperty3 = {};
    property4 = function() {};
    property5 = () => {};
    
    // 構造器
    constructor(...args) {
        super();
        // code here
    };
    
    // 方法methods
    method1() {
        // code here
    };
    method2(...args) {
        //code here
    };
}

可以定義成員屬性和成員方法以及構造器,他們之間都有封號;隔開;

在通過new className()創建物件obj的時候,會立即執行構造器方法;

屬性會成為obj的屬性,句式為賦值陳述句,就算等號右邊是函式,它也依然是一個屬性,注意與方法宣告陳述句區別開;

方法會成為obj的原型里的方法,即放在className.prototype屬性里;

像使用function一樣使用class關鍵字

正如函式運算式一樣,類也有類運算式:

image-20221221213521212

還可以像傳遞一個函式一樣,去傳遞一個類:

image-20221221213652757

這在Java中是不可想象的,但是在JS中,就是這么靈活;

靜態屬性和靜態方法

靜態屬性和靜態方法,不會成為物件的屬性和方法,永遠都屬于類本身,只能通過類去呼叫;

  • 定義語法

    // 直接在類中,通過static關鍵字定義
    class className {
        static property = ...;
        static methoed() {};
    }
    
    // 通過類直接添加屬性和方法,即為靜態的
    class className {};
    className.property = ...;
    className.method = function() {};
    
  • 呼叫語法

    類似于物件呼叫屬性和方法,直接通過類名去呼叫

    className.property;
    className.method();
    

靜態屬性/方法,可以和普通屬性/方法同名,這不會被弄混,因為他們的呼叫者不一樣,前者是類,后者是類物件;

私有屬性和私有方法

JS新增的私有特性,在屬性和方法之前添加#號,使其只在類中可見,物件無法呼叫,只能通過類提供的普通方法去間接訪問;

  • 定義和呼叫語法

    class className {
        // 定義,添加#號
        #property = ...;
        #method() {};
        
        // 只能在類中可見,呼叫也需要加#號
        getProperty() {
            return this.#property;
        }
        set property(value) {
            this.#property = value;
        }
    }
    

注意,#property是一個總體作為屬性名,與property是不同的,#method同理;

在這個私有特性之前,JS采用人為約定的方式,去間接實作私有;

在屬性和方法之前添加下劃線_,約定這樣的屬性和方法,只能在類中可見,只能靠人為遵守這樣的約定;

類檢查instanceof

我們知道,可以用typeof關鍵字來獲取一個變數是什么資料型別;

現在可以用instanceof關鍵字,來判斷一個物件是什么類的實體;

語法obj instanceof className,會回傳一個布林值:

  • 如果classNameobj原型鏈上的類,回傳true;
  • 否則,回傳false;

它是怎么去判斷的呢?假設現在有如下幾個類:

class A {};
class B extends A {};
class C extends B {};
let c = new C();

c的原型是C.prototype

C.prototype的原型是B.prototype

B.prototype的原型是A.prototype

A.prototype的原型是Object.prototype

Object.prototype的原型是null;

原型鏈如上所示;

當我們執行c instanceof A的時候,它是這樣的程序:

c.__proto__ === A.prototype?否,則繼續;

c.__proto__.__proto__ === A.prototype?否,則繼續;

c.__proto__.__proto__.__proto__ === A.prototype?是,回傳true;

如果一直否的話,這個程序會持續下去,直到將c的原型鏈溯源到null,全都不等于A.prototype,則回傳false;

也就是說,instanceof關鍵字,比較的是物件的原型鏈上的原型和目標類的prototype是否相等(原型和prototype里有constructor,但是instanceof不會比較構造器是否相等,只會比較隱藏屬性[[Prototype]]);

靜態方法Symbol.hasInstance

大多數類是沒有實作靜態方法[Symbol.hasInstance]的,如果有一個類實作了這個靜態方法,那么instanceof關鍵字會直接呼叫這個靜態方法;

如果類沒有實作這個靜態方法,那么則會按照上述說的流程去檢查;

class className {
    static [Symbol.hasInstance]() {};
}

objA.isPrototypeOf(objB)

isPrototypeOf()方法,會判斷objA的原型是否處在objB的原型鏈中,如果在則回傳true,否則回傳false;

objA.isPrototypeOf(objB)就相當于objB instanceof classA

反過來,objB instanceof classA就相當于classA.prototype.isPrototypeOf(objB)

繼承

我們知道,JS的繼承,是通過原型來實作的,現在結合原型來說一下類的繼承相關內容,

關鍵字extends

JS中表示繼承的關鍵字是extends,如果classA extends classB,則說明classA繼承classBclassA是子類,classB是父類;

原型高于extends

時刻記住,JS的繼承,是依靠原型來實作的;

關鍵字extends雖然確立了兩個類的父子關系,但是這只是一開始確立子類的父原型;

但是父原型是可以中途被修改的,此時子類呼叫方法,是沿著原型鏈去尋找的,而不是沿著子類父類的關鍵字宣告去尋找的,這和Java是不一樣的:

image-20221223233526394

如圖所示,C extends A確立了C一開始的父原型是A.prototypec.show()呼叫的也是父類A的方法;

但是后面修改c的父原型為B.prototypec.show呼叫的就不是父類A的方法,而是父原型的方法;

也就是說,原型才是核心,高于extends關鍵字;

基類和派生類

class classA {};
class classB extends classA {};

classA這樣沒有繼承任何類(實際上父原型是Object.prototype)的類稱為基類;

classB這樣繼承classB的類,稱為classB的派生類;

為什么要分的這么細,是因為在創建類時,他們兩個的行為不同,后面會說到;

類的原型

類本身也是有原型的,就像類物件有原型一樣;

image-20221223211958950

可以看到,B的原型就是其父類A,而A作為基類,基類的原型是本地方法;

正因如此,B可以通過原型去呼叫A的靜態方法/屬性;

也就是說,靜態方法/屬性,也是可以繼承的,通過類的原型去繼承;

類物件的原型和類的prototype屬性

在創建類物件的時候,會將類的prototype屬性值復制給類物件的原型;

所以說,類物件的原型等于類的prototype屬性值;

image-20221223214052596

而類的prototype屬性,默認就有兩個屬性:

  • 構造器constructor:指向類本身;
  • 原型[[Prototype]]:指向父類的prototype屬性;

以及

  • 類的普通方法;

從上圖中可以看出,A的prototype屬性里,除構造器和原型以外,就只有一個普通方法show()

這說明,只有類的普通方法,會自動進入類的prototype屬性參與繼承;

也就是說,一個類物件的資料結構,如下:

  • 普通屬性
  • (原型)prototype屬性
    • 構造器
    • 父類的prototype屬性(父原型)
    • 方法

另外,類的prototype屬性是不可寫的,但是類物件的原型則是可以修改的;

繼承了哪些東西

當子類去繼承父類的時候,到底繼承到了父類的哪些東西,也即子類可以用父類的哪些內容;

image-20221223220502179

從結果上來看,我們可以確定如下:

  • 子類繼承父類的靜態屬性/方法(基于類的原型);
  • 子類物件繼承父類的普通方法和構造器(基于類的prototype);
  • 子類直接將父類的普通屬性作為自己的普通屬性(普通屬性不參與繼承);

由于原型鏈的存在,這些繼承會一路沿著原型鏈回溯,繼承到所有祖宗類;

同名屬性的覆寫

由于繼承的機制,勢必子類和父類可能會有同名屬性的存在:

image-20221223221756177

從結果上可以看到,雖然子類直接將父類的普通屬性作為自己的普通屬性,但是當出現同名屬性,屬性值會進行覆寫,最終的值采用子類自己定義的值;

同名方法的重寫

與屬性一樣,子類和父類也可能會出現同名方法;

當然大多數情況下,是我們自己要拓展方法功能而故意同名,從而重寫父類的方法;

image-20221223222233351

如上所示,我們重寫了父類的靜態方法和普通方法;

如果是重寫構造器的話,分兩種情況:

// 基類重寫構造器
class A {
    constructor() {
        code...
    }
}
    
// 派生類重寫構造器
class B extends A() {
    constructor() {
        // 一定要先寫super()
        super();
        code...
    }
}

子類的呼叫順序

從上圖還可以看出來,子類呼叫方法的順序:

  • 先從自己的方法里呼叫,發現沒有可呼叫的方法時;
  • 再沿著原型鏈,先從父類開始尋找方法,一直往上溯源,直到找到可呼叫的方法,或者沒有而出錯;

super關鍵字

類的方法里,有一個特殊的、專門用于super關鍵字的特殊屬性[[HomeObject]],這個屬性系結super陳述句所在的類的物件,不會改變;

super關鍵字,則指向[[HomeObject]]系結的物件的類的父類的prototype

這要求,super關鍵字用于派生類類的方法里,基類是不可以使用super的,因為沒有父類;

當我們使用super關鍵字時,借助于[[HomeObject]],總是能夠正確重用父類方法;

image-20221223225446030

如上,super陳述句所在的類為B,其物件為b,即[[HomeObject]]系結b

super則指向b的類的父原型,即A的prototype屬性;

super.show()就類似于A.prototype.show(),故而最終結果如上所示;

可以簡單理解成,super指向子類物件的父類的prototype

構造器constructor

終于說到構造器了,理解了構造器的具體創建物件的程序,我們就能理解關于繼承的很多內容了;

先來看一下基類的構造器創建物件的程序:

image-20221224002125631

執行let a = new A()時,大致流程如下:

  • 首先呼叫A.prototype的特性[[Prototype]]創建一個字面量物件,同時this指標指向這個字面量物件;
  • 然后執行類A()的定義,A定義的普通屬性成為字面量物件的屬性并初始化,A.prototypevalue值復制給字面量物件的隱藏屬性[[Prototype]]
  • 然后再執行constructor構造器,沒有構造器就算了;
  • 回傳this指標給變數a,即a此時參考該字面量物件了;

從結果上看,在執行構造器時,字面量物件就已經有原型了,以及屬性name,且值初始化為tomA

然后才對屬性name重新賦值為jerryA

然而,構造器中對屬性的重新賦值,從一開始就決定好了,只是在執行到這句賦值陳述句之前,暫存在字面量物件中;

現在再來看一下派生類創建物件的程序;

image-20221224005351505

執行let b = new B()的大致流程如下:

  • 首先呼叫B.prototype的特性[[Prototype]]創建一個字面量物件,同時this指標指向這個字面量物件;
  • 然后執行類B()的定義,B定義的普通屬性成為字面量物件的屬性并初始化,B.prototypevalue值復制給字面量物件的隱藏屬性[[Prototype]]
  • 然后再執行constructor構造器(沒有顯式定義構造器會提供默認構造器),第一句super(),開始進入類A()的定義;
    • 暫存B的屬性值,轉而賦值為A定義的值,A.prototypevalue值復制給B.__proto__的隱藏屬性[[Prototype]];
    • 然后執行constructor構造器(基類沒有構造器就算了);
    • 回傳this指標;
    • 丟棄A賦值的屬性值,重新使用暫存的B的屬性值;
  • 繼續執行constructor構造器剩下的陳述句;
  • 回傳this指標給變數b,即b參考該字面量物件了;

通過基類和派生類創建物件的流程對比,可以發現主要區別在于類的屬性的賦值上;

屬性值從一開始就已經暫存好:

  • 如果構造器constructor中有賦值,則暫存這個值;
  • 如果構造器沒有,則暫存類定義中的值;
  • 不管父類及其原型鏈上同名的屬性在中間進行過幾次賦值,最終都會重新覆寫為最開始就暫存好的值;

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/540684.html

標籤:其他

上一篇:第一百一十七篇: JavaScript 工廠模式和原型模式

下一篇:Web 標準 & W3C 規范

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 使用Django Rest framework搭建Blog

    在前面的Blog例子中我們使用的是GraphQL, 雖然GraphQL的使用處于上升趨勢,但是Rest API還是使用的更廣泛一些. 所以還是決定回到傳統的rest api framework上來, Django rest framework的官網上給了一個很好用的QuickStart, 我參考Qu ......

    uj5u.com 2023-04-20 08:17:54 more
  • 記錄-new Date() 我忍你很久了!

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 大家平時在開發的時候有沒被new Date()折磨過?就是它的諸多怪異的設定讓你每每用的時候,都可能不小心踩坑。造成程式意外出錯,卻一下子找不到問題出處,那叫一個煩透了…… 下面,我就列舉它的“四宗罪”及應用思考 可惡的四宗罪 1. Sa ......

    uj5u.com 2023-04-20 08:17:47 more
  • 使用Vue.js實作文字跑馬燈效果

    實作文字跑馬燈效果,首先用到 substring()截取 和 setInterval計時器 clearInterval()清除計時器 效果如下: 實作代碼如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta ......

    uj5u.com 2023-04-20 08:12:31 more
  • JavaScript 運算子

    JavaScript 運算子/運算子 在 JavaScript 中,有一些運算子可以使代碼更簡潔、易讀和高效。以下是一些常見的運算子: 1、可選鏈運算子(optional chaining operator) ?.是可選鏈運算子(optional chaining operator)。?. 可選鏈操 ......

    uj5u.com 2023-04-20 08:02:25 more
  • CSS—相對單位rem

    一、概述 rem是一個相對長度單位,它的單位長度取決于根標簽html的字體尺寸。rem即root em的意思,中文翻譯為根em。瀏覽器的文本尺寸一般默認為16px,即默認情況下: 1rem = 16px rem布局原理:根據CSS媒體查詢功能,更改根標簽的字體尺寸,實作rem單位隨螢屏尺寸的變化,如 ......

    uj5u.com 2023-04-20 08:02:21 more
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 08:01:50 more
  • 如何在 vue3 中使用 jsx/tsx?

    我們都知道,通常情況下我們使用 vue 大多都是用的 SFC(Signle File Component)單檔案組件模式,即一個組件就是一個檔案,但其實 Vue 也是支持使用 JSX 來撰寫組件的。這里不討論 SFC 和 JSX 的好壞,這個仁者見仁智者見智。本篇文章旨在帶領大家快速了解和使用 Vu ......

    uj5u.com 2023-04-20 08:01:37 more
  • 【Vue2.x原始碼系列06】計算屬性computed原理

    本章目標:計算屬性是如何實作的?計算屬性快取原理以及洋蔥模型的應用?在初始化Vue實體時,我們會給每個計算屬性都創建一個對應watcher,我們稱之為計算屬性watcher ......

    uj5u.com 2023-04-20 08:01:31 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:01:10 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:00:32 more