JS深入學習筆記 - 第一章.構造函數原型與原型鏈

来源:https://www.cnblogs.com/zry123/archive/2023/09/14/17701654.html
-Advertisement-
Play Games

1.構造函數和原型 1.1 概述 在典型的 OOP語言中(如Java),都存在類的概念,類就是對象的模板,對象就是類的實例,但在ES6之前,JS並沒有引入類的概念。 在ES6之前,對象不是基於類創建的,而是一種稱為構建函數的特殊函數來定義對象和它們的特征。 有三種創建對象的方式: 對象字面量(con ...


1.構造函數和原型 

1.1 概述

在典型的 OOP語言中(如Java),都存在類的概念,類就是對象的模板,對象就是類的實例,但在ES6之前,JS並沒有引入類的概念。

ES6之前,對象不是基於類創建的,而是一種稱為構建函數的特殊函數來定義對象和它們的特征。

有三種創建對象的方式:

  1. 對象字面量(const obj = {name:'ab'})

  2. new Object()

  3. 自定義構造函數

//構造函數
function Star(uname,age){
​
    this.uname = uname;
    
    this.age = age
    
    this.sing = function(){
    
        console.log("我會唱歌")
    
    }
​
}
const ldh = new Star("劉德華",18)
const syz = new Star("孫燕姿",17)
console.log(ldh)
ldh.sing()
syz.sing()

 

1.2 構造函數

構造函數是一種特殊的函數,主要用來初始化對象,即為對象成員變數賦初始值,它總與new一起使用。我們可以把對象中一些公共的屬性和方法抽取出來,然後封裝到這個函數裡面。

 

在JS中使用構造函數是要註意以下兩點:

  1. 構造函數用於創建某一類對象,其首字母要大寫

  2. 構造函數要和new一起使用才有意義

     

new在執行時會做四件事情:

  1. 在記憶體中創建一個新的空對象

  2. 讓this指向這個新的對象

  3. 執行構造函數裡面的代碼,給這個新對象添加屬性和方法

  4. 返回這個新對象(所有構造函數裡面不需要renturn)

 

1.3 實例成員和靜態成員

實例成員:

構造函數內部通過this添加的成員 ,如下圖的uname,age,sing就是實例成員

實例成員只能通過實例化的對象來訪問

function Star(uname,age){
​
    this.uname = unamw;
​
    this.age = age
​
    this.sing = function(){
​
        console.log("我會唱歌")
​
    }
}
const ldh = new Star("劉德華",18)

靜態成員:

靜態成員在構造函數本身上添加的成員,如下圖的sex就是靜態成員

鏡頭成員只能通過構造函數來訪問不能通過對象訪問。

Star.sex = '男'
​
console.log('Star.sex')

 

1.4構造函數的問題

構造函數方法很好用,但存在浪費記憶體的問題

如:Star()構造函數中的sing()方法在每次實例化對象的時候都需要單獨開闢一份記憶體空間,存在浪費空間的問題。

但是我們希望所有的對象使用同一個函數,這樣就比較節省記憶體。

  //構造函數
    function Star(uname, age) {
      this.uname = uname;
​
      this.age = age;
​
      this.sing = function () {
        console.log("我會唱歌");
      };
    }
    
    const ldh = new Star("劉德華", 18);
    const syz = new Star("孫燕姿", 17);

 

思考:可是,為什麼每次實例化都是單獨開闢空間呢?

這個問題我查閱了很多的資料,總結一句話就是:在JS中,引用類型被創建的時候都會開闢一個新的空間。(其中的知識點比較多,詳情請期待下一篇文章~)

 

1.5 構造函數原型prototype

構造函數通過原型分配的函數是所有對象所共用的。


JavaScript規定,每一個構造函數都有一個prototype 屬性,指向另一個對象。

註意: 這個prototype就是一個對象,這個對象所有的屬性和方法都會被構造函數所擁有

我們可以把那些不變的方法,直接定義在prototype 對象上,這樣所有對象的實例就可以共用這些方法

function Star(uname,age){
​
    this.uname = unamw;
    
    this.age = age
}
Star.sing = function(){
    console.log("我會唱歌")
}
​
const syz = new Star('孫燕姿',20)
​
syz.sing()//我會唱歌

   
  1. 原型是什麼? -------是一個對象,我們也稱prototype為原型對象

  2. 原型的作用是什麼? ------共用方法

 

1.6對象原型 __ proto __

對象都會有一個屬性 __ prpto __ 指向構造函數的 prototype 原型對象,之所以對象可以使用構造函數 prototype 原型對象的屬性和方法,就是因為對象有__ proto __ 原型的存在。

總結:
  • __ proto __ 對象原型和原型對象 prototype 是等價的

function Star(uname,age){
​
    this.uname = unamw;
    
    this.age = age
}
Star.sing = function(){
    console.log("我會唱歌")
}
​
const syz = new Star('孫燕姿',20)
console.log(syz.__ptoto === Star.prototype)//等價

 

  • __ proto __ 對象原型的意義就在於為對象的查找機制提供一個方向或者一條線路,但是它是一個非標準屬性,因此實際開發中,不可以使用這個屬性,它只是內部指向原型對象prototype。

     

1.7 constructor構造函數

對象原型(__ proto __)和構造函數原型對象(prototype)裡面都有一個屬性:constructor屬性,constructor 我們稱為構造函數,因為它指回構造函數本身

function Star(uname,age){
​
    this.uname = unamw;
    
    this.age = age
}
Star.prototype = {
    //這種情況下我們已經修改了原來prototype,給原型對象賦值的是一個對象,所有必須手動的把 constructor指回原來的構造函數
    constructor:Star,
    sing:function(){
        console.log("唱歌")
    }
    movie:function(){
        console.log("電影")
    }
}

 

 

1.8構造函數實例原型對象三者之間的關係

 

 

總結:構造函數和原型對象之間有互相可以表明對方身份的”信物“:prototype 和 constructor。

1.9 原型鏈

 

 

總結:

ldh對象實例的原型(__ proto __)可以找到它的對象原型——Star原型對象prototype;

通過Star原型對象prototype的原型(__ proto __),可以找到它的對象原型——Object原型對象 prototype;

通過Object原型對象 prototype的原型(__ proto __),可以找到它的對象原型——null(最頂層)。

因此形成的線路叫做原型鏈,為我們提供了某個屬性或者函數的查找的線路

1.10 原型鏈查找規則

  1. 當訪問一個對象的屬性(包括方法)時,首先查找這個對象自身有沒有該屬性

  2. 如果沒有就查找它的原型(也就是__ proto __ 指向的prototype原型對象

  3. 如果還沒有就查找原型對象的原型Object的原型對象

  4. 以此類推一直找到最頂層(null)

1.11原型對象的this指向

function Star(uname,age){
​
    this.uname = unamw;
    
    this.age = age
}
​
let that;
 Star.prototype.sing = function(){
     that = this
    console.log("我會唱歌")
 }
const ldh = new Star("劉德華",18)
ldh.sing()
console.log(that === ldh)//true

 


構造函數(star)中 this 指向 創建的實例對象(ldh)。

sing函數只有調用之後才能確然this的指向,一般原則是:誰調用,this指向誰。

 

1.12 原型對象的應用

擴展內置對象

可以通過原型對象對原來的內置對象進行擴展自定義的方法。比如:給數組增加自定義求和的功能

Array.prototype.sum = function(){
    let sum = 0
    for(let i = 0;i < this.length; i++){
        sum += this[i]
    }
    return sum;
}
let arr1 = [1,4,5,6,8,9]
​
console.log(arr1.sum())//33
cosole.log(Array.prototype)//可以在arry中看到擴展的求和方法

 

2.繼承

ES6 之前並沒有給我們提供extends繼承。我們可以通過構造函數+原型對象模擬實現繼承,被稱為組合繼承


2.1call()

調用這個函數,並且修改函數運行時的this指向

fun.call(thisArg, arg1, arg2... )

  • thisArg:當前調用函數this的指向對象

  • arg1 ,arg2: 傳遞的其他對象

const o = {
    name: 'zooey'
}
function fn(x,y){
    console.log("輸出這個函數")
    console.log(this) // 此時 this 是 window
    console.log(x+y)
}
//普通調用的方式
fn()
​
//call調用方式: 使用對象調用, 此時的 this 是 o
fn.call(o) //此時 this 輸出是 O
​
//call 做一些其他操作 比如:求出後兩個參數的和
fn.call(o,5,2) //此時 this 輸出是 O 

2.2借用構造函數繼承父類屬性

核心原理:通過call 把父類的this 指向子類型的this ,這樣就可以實現子類型繼承夫類型的屬性。

    function Father(uname, age) {
      this.uname = uname;
      this.age = age;
    }
​
    function Son(uname, age, score) {
      /**
      使用call調用Father 構造方法,
      並且把Father構造方法的this ,修改為Son的調用者
      傳遞參數uname,age,將參數也綁定給Father
      */
      Father.call(this, uname, age);
      this.score = score;
    }
​
    const son = new Son("lan", 26, 100);
    console.log(son);

 

註意點:

在 Son 構造函數中第一個參數是將 Son 的this傳遞給Father ,但是Son只有在實例化時才能知道this是誰。

 

初始化的參數通過Son傳遞給Father

 

 

2.3借用原型對象繼承父類型方法

此處有一個思考:

在下圖中,Array的原型對象中增加求和方法sum,在實例對arr1中就可以直接使用,但是在非實例化的形式中,如何實現方法的繼承呢?

 

案例

function Father(uname, age) {
      this.uname = uname;
      this.age = age;
    }
​
    Father.prototype.getMoney = function () {
      console.log("掙錢");
    };
​
    function Son(uname, age, score) {
      /**
      使用call調用Father 構造方法,
      並且把Father構造方法的this ,修改為Son的調用者
      傳遞參數uname,age,將參數也綁定給Father
      */
      Father.call(this, uname, age);
      this.score = score;
    }
    Son.prototype.exam = function () {
      console.log("孩子考試");
    };
​
    const son = new Son("lan", 26, 100);
    console.log(son);
    son.exam();
    son.getMoney();

 

 

| 可以看到上圖中,son實例對象不能調用getMoney()方法

| 也不能將Father的prototype直接賦值給Son ,如下圖

//直接賦值的操作
Son.prototype = Father.prototype;
Son.prototype.exam = function () {
      console.log("孩子考試");
 };
//此時對Son的prototype修改也會是 Father擁有 exam方法,因為現在是: Son的prototype 指向了Father 的 prototype,兩個構造函數本質上是一個prototype.
console.log(Father);

 

註意: 此時對Son的prototype修改也會是 Father擁有 exam方法,因為現在是: Son的prototype 指向了Father 的 prototype,兩個構造函數本質上是一個prototype.

正確的做法:

Son.prototype = new Father();
//記得將constructor指回 Son構造函數
Son.prototype.constructor = Son;
​
Son.prototype.exam = function () {
    console.log("孩子考試");
};
console.log(Father);

思考:記得將constructor指回 Son構造函數 的原因是什麼呢?

嘗試註釋掉指回構造函數的代碼,也可以正常輸出,只不過沒有constructor參數

只是son實例可以歸溯自己的構造函數是誰.


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文分享自華為雲社區《GaussDB(DWS)性能調優:Sort+Groupagg聚集引起的性能瓶頸案例》,作者: O泡果奶~ 。 本文針對SQL語句長時間執行不出來,且verbose執行計劃中出現Sort+GroupAgg聚集方式的案例進行分析。 1、【問題描述】 語句執行時間過長,2300s+也 ...
  • 自 3.0 版本發佈以來,在研發人員和社區用戶的不斷努力下,TDengine 做了大量更新,產品穩定性和易用性也在不斷提升。近日,TDengine 3.1.1.0 成功發佈,本文將向大家簡單介紹一下該版本涉及的重大更新。 寫在前面 伴隨 2023 年 9 月官網改版,TDengine 正式升級為高性 ...
  • 分享的 HTML 與上圖內容一樣,需要修改的小伙伴可以自行修改內容。 <style><!-- @import url("https://fonts.googleapis.com/css?family=Share+Tech+Mono|Montserrat:700"); * { margin: 0; p ...
  • 介紹 ESLint 是一個根據方案識別並報告 ECMAScript/JavaScript 代碼問題的工具,其目的是使代碼風格更加一致並避免錯誤。在很多地方它都與 JSLint 和 JSHint 類似,除了: ESLint 使用 Espree 對 JavaScript 進行解析。 ESLint 在代碼 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 在 Web 開發中,非同步請求是一個常見的操作。然而,在非同步請求中正確地獲取返回值卻可能會變得棘手。本文將介紹如何解決非同步請求中的返回值問題,並提供一種解決方案。 一、問題描述 在某個 Web 應用程式中,用戶遇到了無法正確獲取非同步請求返回 ...
  • 介紹 在本文中,我試圖以最簡潔的方式來闡明JavaScript編程原理中對象類型賦值和原生類型賦值之間的區別,以及它們各自是如何工作的。這也是我希望在我的JavaScript編程生涯早期就已經理解的東西。 JS中的原生類型和對象類型 首先,讓我們回顧一下JavaScript中不同的原生類型和對象類型 ...
  • 3.類和對象 3.1面向對象 這裡順帶提一句學習JAVA時,老師說的面向對象和麵向過程的區別: 面向過程:強調做什麼事情,具體什麼步驟。舉個把大象放進冰箱的例子: 打開冰箱門 把大象放進冰箱 關上冰箱門 面向對象:強調的是做動作的主體(稱之為對象) 冰箱:打開操作 冰箱:放的操作(放的可以是大象也可 ...
  • 我的小冊 《CSS 技術揭秘與實戰通關》上線了,想瞭解更多有趣、進階、系統化的 CSS 內容,可以猛擊 - LINK。 本文,我們將一起利用純 CSS,實現如下這麼個酷炫的效果: 在一年前,我介紹了 CSS 中非常新奇有趣的一個新特性 -- @scroll-timeline:革命性創新,動畫殺手鐧 ...
一周排行
    -Advertisement-
    Play Games
  • 下麵是一個標準的IDistributedCache用例: public class SomeService(IDistributedCache cache) { public async Task<SomeInformation> GetSomeInformationAsync (string na ...
  • 這個庫提供了在啟動期間實例化已註冊的單例,而不是在首次使用它時實例化。 單例通常在首次使用時創建,這可能會導致響應傳入請求的延遲高於平時。在註冊時創建實例有助於防止第一次Request請求的SLA 以往我們要在註冊的時候實例單例可能會這樣寫: //註冊: services.AddSingleton< ...
  • 最近公司的很多項目都要改單點登錄了,不過大部分都還沒敲定,目前立刻要做的就只有一個比較老的項目 先改一個試試手,主要目標就是最短最快實現功能 首先因為要保留原登錄方式,所以頁面上的改動就是在原來登錄頁面下加一個SSO登錄入口 用超鏈接寫的入口,頁面改造後如下圖: 其中超鏈接的 href="Staff ...
  • Like運算符很好用,特別是它所提供的其中*、?這兩種通配符,在Windows文件系統和各類項目中運用非常廣泛。 但Like運算符僅在VB中支持,在C#中,如何實現呢? 以下是關於LikeString的四種實現方式,其中第四種為Regex正則表達式實現,且在.NET Standard 2.0及以上平... ...
  • 一:背景 1. 講故事 前些天有位朋友找到我,說他們的程式記憶體會偶發性暴漲,自己分析了下是非托管記憶體問題,讓我幫忙看下怎麼回事?哈哈,看到這個dump我還是非常有興趣的,居然還有這種游戲幣自助機類型的程式,下次去大玩家看看他們出幣的機器後端是不是C#寫的?由於dump是linux上的程式,剛好win ...
  • 前言 大家好,我是老馬。很高興遇到你。 我們為 java 開發者實現了 java 版本的 nginx https://github.com/houbb/nginx4j 如果你想知道 servlet 如何處理的,可以參考我的另一個項目: 手寫從零實現簡易版 tomcat minicat 手寫 ngin ...
  • 上一次的介紹,主要圍繞如何統一去捕獲異常,以及為每一種異常添加自己的Mapper實現,並且我們知道,當在ExceptionMapper中返回非200的Response,不支持application/json的響應類型,而是寫死的text/plain類型。 Filter為二方包異常手動捕獲 參考:ht ...
  • 大家好,我是R哥。 今天分享一個爽飛了的面試輔導 case: 這個杭州兄弟空窗期 1 個月+,面試了 6 家公司 0 Offer,不知道問題出在哪,難道是杭州的 IT 崩盤了麽? 報名面試輔導後,經過一個多月的輔導打磨,現在成功入職某上市公司,漲薪 30%+,955 工作制,不咋加班,還不捲。 其他 ...
  • 引入依賴 <!--Freemarker wls--> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.30</version> </dependency> ...
  • 你應如何運行程式 互動式命令模式 開始一個互動式會話 一般是在操作系統命令行下輸入python,且不帶任何參數 系統路徑 如果沒有設置系統的PATH環境變數來包括Python的安裝路徑,可能需要機器上Python可執行文件的完整路徑來代替python 運行的位置:代碼位置 不要輸入的內容:提示符和註 ...