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
  • 1、預覽地址:http://139.155.137.144:9012 2、qq群:801913255 一、前言 隨著網路的發展,企業對於信息系統數據的保密工作愈發重視,不同身份、角色對於數據的訪問許可權都應該大相徑庭。 列如 1、不同登錄人員對一個數據列表的可見度是不一樣的,如數據列、數據行、數據按鈕 ...
  • 前言 上一篇文章寫瞭如何使用RabbitMQ做個簡單的發送郵件項目,然後評論也是比較多,也是準備去學習一下如何確保RabbitMQ的消息可靠性,但是由於時間原因,先來說說設計模式中的簡單工廠模式吧! 在瞭解簡單工廠模式之前,我們要知道C#是一款面向對象的高級程式語言。它有3大特性,封裝、繼承、多態。 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 介紹 Nodify是一個WPF基於節點的編輯器控制項,其中包含一系列節點、連接和連接器組件,旨在簡化構建基於節點的工具的過程 ...
  • 創建一個webapi項目做測試使用。 創建新控制器,搭建一個基礎框架,包括獲取當天日期、wiki的請求地址等 創建一個Http請求幫助類以及方法,用於獲取指定URL的信息 使用http請求訪問指定url,先運行一下,看看返回的內容。內容如圖右邊所示,實際上是一個Json數據。我們主要解析 大事記 部 ...
  • 最近在不少自媒體上看到有關.NET與C#的資訊與評價,感覺大家對.NET與C#還是不太瞭解,尤其是對2016年6月發佈的跨平臺.NET Core 1.0,更是知之甚少。在考慮一番之後,還是決定寫點東西總結一下,也回顧一下.NET的發展歷史。 首先,你沒看錯,.NET是跨平臺的,可以在Windows、 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 添加節點(nodes) 通過上一篇我們已經創建好了編輯器實例現在我們為編輯器添加一個節點 添加model和viewmode ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...
  • 類型檢查和轉換:當你需要檢查對象是否為特定類型,並且希望在同一時間內將其轉換為那個類型時,模式匹配提供了一種更簡潔的方式來完成這一任務,避免了使用傳統的as和is操作符後還需要進行額外的null檢查。 複雜條件邏輯:在處理複雜的條件邏輯時,特別是涉及到多個條件和類型的情況下,使用模式匹配可以使代碼更 ...
  • 在日常開發中,我們經常需要和文件打交道,特別是桌面開發,有時候就會需要載入大批量的文件,而且可能還會存在部分文件缺失的情況,那麼如何才能快速的判斷文件是否存在呢?如果處理不當的,且文件數量比較多的時候,可能會造成卡頓等情況,進而影響程式的使用體驗。今天就以一個簡單的小例子,簡述兩種不同的判斷文件是否... ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...