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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...