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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...